jason810496 commented on code in PR #76005:
URL: https://github.com/apache/airflow/pull/76005#discussion_r4421302440
##########
ts-sdk/src/coordinator/log-channel.ts:
##########
@@ -49,28 +49,107 @@ const DEFAULT_LOGGER_NAME = "ts-sdk";
const CLOSE_FLUSH_TIMEOUT_MS = 3_000;
+// Level names match Airflow's Python parser: structlog's NAME_TO_LEVEL plus
the `fatal` alias
+// that configure_logging adds.
+const LOG_LEVELS = new Map([
+ ["NOTSET", 0],
+ ["DEBUG", 10],
+ ["INFO", 20],
+ ["WARN", 30],
+ ["WARNING", 30],
+ ["ERROR", 40],
+ ["EXCEPTION", 40],
+ ["CRITICAL", 50],
+ ["FATAL", 50],
+]);
+
+const DEFAULT_LOG_LEVEL = LOG_LEVELS.get("INFO")!;
+
+function parseLogLevel(value: string | undefined): number | undefined {
+ return LOG_LEVELS.get(value?.trim().toUpperCase() ?? "");
+}
+
+/** Parse `namespace_levels`: `<logger>=<level>` pairs separated by whitespace
and/or commas.
+ * Later duplicates win; malformed entries are returned so the caller can
report them. */
+function parseNamespaceLevels(value: string | undefined): {
+ levels: Map<string, number>;
+ invalid: string[];
+} {
+ const levels = new Map<string, number>();
+ const invalid: string[] = [];
+ for (const entry of (value ?? "").split(/[\s,]+/).filter(Boolean)) {
+ const separator = entry.indexOf("=");
+ const level = parseLogLevel(entry.slice(separator + 1));
+ if (separator <= 0 || level === undefined) {
+ invalid.push(entry);
+ } else {
+ levels.set(entry.slice(0, separator), level);
+ }
+ }
+ return { levels, invalid };
+}
+
+/** Threshold for `name`: the most specific configured dotted ancestor, else
the global level. */
+function resolveLogLevel(shared: LogChannelState, name: string): number {
+ let namespace = name;
+ while (namespace) {
+ const level = shared.namespaceLevels.get(namespace);
+ if (level !== undefined) return level;
+ const separator = namespace.lastIndexOf(".");
+ if (separator < 0) break;
+ namespace = namespace.slice(0, separator);
+ }
+ return shared.level;
+}
+
interface LogChannelState {
sock: Socket | null;
connected: boolean;
closed: boolean;
buffer: string[];
+ level: number;
+ namespaceLevels: ReadonlyMap<string, number>;
}
export class LogChannel {
private readonly shared: LogChannelState;
private readonly name: string;
private readonly isRoot: boolean;
+ // Configuration is fixed when the root is created, so resolve this
channel's threshold once.
+ private readonly threshold: number;
Review Comment:
How about just using `logLevel` instead of introducing a new term?
```suggestion
private readonly threshold: logLevel;
```
##########
ts-sdk/src/coordinator/log-channel.ts:
##########
@@ -116,13 +195,21 @@ export class LogChannel {
}
send(
- record: Omit<LogRecord, "timestamp" | "logger"> & {
+ record: Pick<LogRecord, "event" | "level"> & {
timestamp?: string;
logger?: string;
+ [key: string]: unknown;
},
): void {
// Drop late records after the log socket has closed.
if (this.shared.closed) return;
+ // The supervisor forwards records unchanged; filtering belongs in the
subprocess.
+ // Apply it before buffering so startup and stderr fallback logs follow
the same rules.
+ // Filter by this channel's name, which is what the `[name]` event prefix
shows. A level
+ // outside the LogLevel type (only reachable from untyped callers) is
treated as INFO.
Review Comment:
It's overstate.
```suggestion
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]