File size: 3,920 Bytes
4c76b0d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
// Runtime logging tests cover plugin runtime log routing and verbosity behavior.
import { beforeEach, describe, expect, it, vi } from "vitest";

const loggingMocks = vi.hoisted(() => {
  const childLogger = {
    debug: vi.fn(),
    info: vi.fn(),
    warn: vi.fn(),
    error: vi.fn(),
  };
  const isFileLogLevelEnabled = vi.fn((_level: string) => true);
  return {
    childLogger,
    getChildLogger: vi.fn(() => childLogger),
    isFileLogLevelEnabled,
  };
});

vi.mock("../../globals.js", () => ({
  shouldLogVerbose: vi.fn(() => false),
}));

vi.mock("../../logging.js", () => ({
  getChildLogger: loggingMocks.getChildLogger,
  isFileLogLevelEnabled: loggingMocks.isFileLogLevelEnabled,
}));

let createRuntimeLogging: typeof import("./runtime-logging.js").createRuntimeLogging;

beforeEach(async () => {
  vi.clearAllMocks();
  loggingMocks.getChildLogger.mockReturnValue(loggingMocks.childLogger);
  loggingMocks.isFileLogLevelEnabled.mockReturnValue(true);
  ({ createRuntimeLogging } = await import("./runtime-logging.js"));
});

describe("createRuntimeLogging", () => {
  it("forwards structured metadata to child loggers", () => {
    const logging = createRuntimeLogging();
    const logger = logging.getChildLogger({ plugin: "discord" }, { level: "warn" });
    const meta = {
      errorName: "Error",
      errorCauseName: "TypeError",
    };

    logger.debug?.("debug details", meta);
    logger.info("info details", meta);
    logger.warn("warn details", meta);
    logger.error("error details", meta);

    expect(loggingMocks.getChildLogger).toHaveBeenCalledWith(
      { plugin: "discord" },
      { level: "warn" },
    );
    expect(loggingMocks.childLogger.debug).toHaveBeenCalledWith(meta, "debug details");
    expect(loggingMocks.childLogger.info).toHaveBeenCalledWith(meta, "info details");
    expect(loggingMocks.childLogger.warn).toHaveBeenCalledWith(meta, "warn details");
    expect(loggingMocks.childLogger.error).toHaveBeenCalledWith(meta, "error details");

    logger.info("message without metadata");
    logger.warn("message with empty metadata", {});
    expect(loggingMocks.childLogger.info).toHaveBeenLastCalledWith("message without metadata");
    expect(loggingMocks.childLogger.warn).toHaveBeenLastCalledWith("message with empty metadata");
  });

  it("resolves the child logger per call so a runtime log-level change takes effect", () => {
    const logging = createRuntimeLogging();
    // Mirror a long-lived channel monitor: capture the logger once, log later.
    const logger = logging.getChildLogger({ module: "mattermost" });

    // Level is below debug when the monitor starts: the write is dropped.
    loggingMocks.isFileLogLevelEnabled.mockReturnValue(false);
    logger.debug?.("dropped before debug enabled");
    expect(loggingMocks.childLogger.debug).not.toHaveBeenCalled();
    expect(loggingMocks.getChildLogger).not.toHaveBeenCalled();

    // Log level raised to debug on the running gateway: the same captured logger
    // must now write, because it re-resolves the child logger per call.
    loggingMocks.isFileLogLevelEnabled.mockReturnValue(true);
    logger.debug?.("written after debug enabled");
    expect(loggingMocks.getChildLogger).toHaveBeenCalledWith({ module: "mattermost" }, undefined);
    expect(loggingMocks.childLogger.debug).toHaveBeenCalledWith("written after debug enabled");
  });

  it("pre-gates on the current file level when no override is set", () => {
    loggingMocks.isFileLogLevelEnabled.mockImplementation((level: string) => level !== "debug");
    const logging = createRuntimeLogging();
    const logger = logging.getChildLogger({ module: "mattermost" });

    logger.debug?.("debug suppressed at info");
    logger.info("info written at info");

    expect(loggingMocks.childLogger.debug).not.toHaveBeenCalled();
    expect(loggingMocks.childLogger.info).toHaveBeenCalledWith("info written at info");
  });
});