• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

variablesoftware / logface / 15371407231

01 Jun 2025 05:16AM UTC coverage: 91.974% (+0.5%) from 91.47%
15371407231

push

github

px
✨ fix: (config): add new logface configuration with emoji sets and randomization

256 of 293 branches covered (87.37%)

Branch coverage included in aggregate %.

443 of 467 relevant lines covered (94.86%)

33.84 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.67
/src/core/emitLog.ts
1
/**
2
 * Emit a formatted log message at the specified level.
3
 * Handles filtering, prefix formatting, and routing to the appropriate console method.
4
 * @param level - The log level
5
 * @param args - Arguments to log
6
 * @param options - Optional per-call log options
7
 * @internal
8
 */
9
import type { LogLevel, LogOptions } from "../types/Logger";
10
import { getCallerTag } from "../utils/getCallerTag.js";
1✔
11
import { matchesScopeFilter } from "../utils/matchesScopeFilter.js";
1✔
12
import {
1✔
13
  emojiForLevel,
14
  colorForLevel,
15
  styleTag,
16
  styleTimestamp,
17
} from "../utils/colorForLevels/colorForLevel.js";
18
import { formatWithReplacements } from "../utils/formatWithReplacements.js";
1✔
19
import { globalLogOptions } from "../core/globalLogOptions.js";
1✔
20

21
/**
22
 * Runtime log level. Only logs at or above this level will be emitted (unless LOG/LOG_VERBOSE is set).
23
 * Can be set at runtime: log.level = 'warn'
24
 */
25
// Place 'log' between 'info' and 'warn' for standard inclusivity
26
const logLevelOrder = [
1✔
27
  "debug",
1✔
28
  "info",
1✔
29
  "log",
1✔
30
  "warn",
1✔
31
  "error",
1✔
32
  "silent",
1✔
33
] as const;
1✔
34
let runtimeLogLevel: LogLevel | "silent" = "debug";
1✔
35

36
export function setLogLevel(level: LogLevel | "silent") {
1✔
37
  runtimeLogLevel = level;
35✔
38
}
35✔
39

40
export function getLogLevel(): LogLevel | "silent" {
1✔
41
  return runtimeLogLevel;
2✔
42
}
2✔
43

44
// --- Generic test suite identifier tags ---
45
export const testIdVars = [
1✔
46
  "VITEST_POOL_ID",
1✔
47
  "VITEST_WORKER_ID",
1✔
48
  // Add more keys here for other test runners as needed
49
];
1✔
50

51
export function emitLog(
1✔
52
  level: LogLevel,
114✔
53
  args: unknown[],
114✔
54
  options?: LogOptions,
114✔
55
): void {
114✔
56
  // Environment variables for log filtering. Uses process.env if available, otherwise empty object.
57
  const env = typeof process !== "undefined" && process.env ? process.env : {};
114!
58
  const testTags = testIdVars
114✔
59
    .map((key) => (env[key] ? `[${key.toLowerCase().replace(/_id$/, '').replace('vitest_', '')}:${env[key]}]` : ""))
114!
60
    .filter(Boolean)
114✔
61
    .join("");
114✔
62
  // Only filter debug logs if LOG/LOG_VERBOSE is set
63
  const filter = env.LOG || env.LOG_VERBOSE || "";
114✔
64
  // Support both comma and semicolon as delimiters for LOG patterns
65
  const filters = filter
114✔
66
    .split(/[,;]+/)
114✔
67
    .map((s: string) => s.trim())
114✔
68
    .filter((s: string) => Boolean(s));
114✔
69

70
  const effectiveOptions = { ...globalLogOptions, ...options };
114✔
71
  const tag = effectiveOptions.tag || getCallerTag();
114✔
72

73
  let shouldLog = true;
114✔
74
  if (filters.length > 0) {
114✔
75
    // Filter out patterns that are not valid (must contain at least one alphanumeric or wildcard)
76
    const validPattern = (p: string) => /[\w*]/.test(p.replace(/^!/, ''));
69✔
77
    const validFilters = filters.filter(validPattern);
69✔
78
    if (validFilters.length === 0) {
69✔
79
      // All patterns are invalid: match nothing
80
      shouldLog = false;
4✔
81
    } else {
68✔
82
      // Separate negated and positive patterns
83
      const negated = validFilters.filter((p: string) => p.startsWith('!'));
65✔
84
      const positive = validFilters.filter((p: string) => !p.startsWith('!'));
65✔
85
      const tagMatch = positive.some((p: string) => matchesScopeFilter(tag, p));
65✔
86
      const levelMatch = positive.some((p: string) => matchesScopeFilter(level, p));
65✔
87
      const positiveMatch = tagMatch || levelMatch;
65✔
88
      const negationMatch = negated.some((p: string) => {
65✔
89
        const pat = p.slice(1);
5✔
90
        return matchesScopeFilter(tag, pat) || matchesScopeFilter(level, pat);
5✔
91
      });
65✔
92
      // Only suppress if a negation matches AND no positive matches
93
      if (negationMatch && !positiveMatch) {
65✔
94
        shouldLog = false;
1✔
95
      } else if (positive.length > 0) {
65✔
96
        shouldLog = positiveMatch;
64✔
97
      } else {
64!
98
        shouldLog = !negationMatch;
×
99
      }
×
100
    }
65✔
101
  } else {
114✔
102
    // Use runtime log level if no LOG/LOG_VERBOSE
103
    const minLevelIdx = logLevelOrder.indexOf(runtimeLogLevel);
45✔
104
    const msgLevelIdx = logLevelOrder.indexOf(level);
45✔
105
    shouldLog =
45✔
106
      minLevelIdx !== -1 &&
45✔
107
      msgLevelIdx !== -1 &&
45✔
108
      msgLevelIdx >= minLevelIdx &&
45✔
109
      runtimeLogLevel !== "silent";
34✔
110
    // For debug, also require DEBUG=1 if runtimeLogLevel is 'debug'
111
    if (level === "debug" && runtimeLogLevel === "debug" && env.DEBUG !== "1") {
45✔
112
      shouldLog = false;
1✔
113
    }
1✔
114
  }
45✔
115

116
  if (!shouldLog) return;
114✔
117

118
  const ts = effectiveOptions.timestamp ? `[${new Date().toISOString()}] ` : "";
114✔
119
  const lvl = effectiveOptions.levelShort
114✔
120
    ? level === "log"
75✔
121
      ? "L"
6✔
122
      : level[0].toUpperCase()
69✔
123
    : level.toUpperCase();
4✔
124
  const emoji = emojiForLevel(level);
114✔
125
  // Prepend testTags to the prefix
126
  const prefix = `${ts ? styleTimestamp(ts) : ""}${testTags}[${lvl}][${styleTag(tag)}]${emoji ? " " + emoji : ""}`;
114✔
127
  const color = colorForLevel(level);
114✔
128
  const target = level === "log" ? console.log : console[level];
114✔
129
  let outArgs = args;
114✔
130
  if (
114✔
131
    args.length > 0 &&
114✔
132
    typeof args[0] === "string" &&
78✔
133
    /%[sdifoO]/.test(args[0])
78✔
134
  ) {
114✔
135
    outArgs = formatWithReplacements(args[0] as string, args.slice(1));
1✔
136
  }
1✔
137
  target(color(prefix), ...outArgs);
79✔
138
}
79✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc