Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 4 additions & 2 deletions apps/landing/src/components/docs/LanguageTabs.astro
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ interface Tab {
interface Props {
tabs: Tab[];
label?: string;
/** Storage key for the remembered pick. Groups that aren't a language choice pass their own. */
group?: string;
}

const { tabs, label = "Language" } = Astro.props;
const { tabs, label = "Language", group = "maple-docs-language" } = Astro.props;
const hasLogo = (id: string): id is LogoId => (LOGO_IDS as readonly string[]).includes(id);
---

<div class="lt" data-lang-tabs="maple-docs-language">
<div class="lt" data-lang-tabs={group}>
<div class="lt__rail" role="tablist" aria-label={label}>
{tabs.map((tab, index) => (
<button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ import logging
logging.getLogger(__name__).warning("Payment retry for order %s", order_id)
```

The root logger's level still applies. Python defaults it to `WARNING`, so call `logging.basicConfig(level=logging.INFO)` or set the level yourself to send `INFO` records.
The root logger's level still applies. Python defaults it to `WARNING`, so call `logging.getLogger().setLevel(logging.INFO)` to send `INFO` records. `logging.basicConfig(level=...)` does nothing once the root logger has a handler, so after `import telemetry` it no longer sets the level. See [Python logging levels](/guides/python-logging-levels) for per-module and per-handler levels.

## Verify

Expand Down
130 changes: 127 additions & 3 deletions apps/landing/src/lib/guide-highlight.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
// Tiny highlighter for guide code blocks. The token classes match the colors a
// guide uses in its diagrams (metric name, labels, value), so a block reads the
// same way as the annotated example above it. Style the `tok-*` classes on the page.
export type GuideLang = "exposition" | "promql" | "yaml" | "python" | "typescript" | "go" | "rust" | "java" | "csharp" | "markdown";
export type GuideLang =
| "exposition"
| "promql"
| "nrql"
| "yaml"
| "console"
| "python"
| "typescript"
| "go"
| "rust"
| "java"
| "csharp"
| "markdown"
| "javascript"
| "json"
| "shell"
| "python-logging"
| "loguru-output";

const escape = (s: string) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
const span = (cls: string, text: string) => `<span class="tok-${cls}">${escape(text)}</span>`;
Expand Down Expand Up @@ -45,6 +62,16 @@ const promql = (code: string) =>
[/[a-zA-Z_:][\w:]*/y, (m) => span("name", m[0])],
]);

// New Relic Query Language: clauses and functions as fn, event types and
// attributes as names, quoted values as labels.
const nrql = (code: string) =>
tokenize(code, [
[/'[^'\n]*'/y, (m) => span("labels", m[0])],
[/\b(?:FROM|SELECT|WHERE|FACET|SINCE|UNTIL|TIMESERIES|LIMIT|AND|OR|AS|ago|months?|days?|hours?|sum|latest|count|average|max|min|uniqueCount|rate)\b/y, (m) => span("fn", m[0])],
[/\b\d+(?:\.\d+)?\b/y, (m) => span("value", m[0])],
[/[a-zA-Z_][\w.]*/y, (m) => span("name", m[0])],
]);

const exposition = (code: string) =>
code
.split("\n")
Expand All @@ -71,12 +98,23 @@ const python = (code: string) =>
tokenize(code, [
[/#.*/y, (m) => span("comment", m[0])],
[/"[^"\n]*"/y, (m) => span("labels", m[0])],
[/\b(?:from|import|def|with|as|return)\b/y, (m) => span("fn", m[0])],
[/\b(?:from|import|def|with|as|return|if|else|for|in|not|is|None|True|False|lambda|try|except|raise)\b/y, (m) => span("fn", m[0])],
[/\b(?:Counter|Gauge|Histogram|Summary)\b/y, (m) => span("name", m[0])],
[/\b\d+(?:\.\d+)?\b/y, (m) => span("value", m[0])],
[/[a-zA-Z_]\w*/y, (m) => escape(m[0])],
]);

// Terminal sessions: "$ command # note" lines; every other line is program output.
const consoleSession = (code: string) =>
code
.split("\n")
.map((line) => {
const cmd = /^(\$ )(.*?)(\s+#.*)?$/.exec(line);
if (!cmd) return escape(line);
return span("comment", cmd[1]) + span("fn", cmd[2]) + (cmd[3] ? span("comment", cmd[3]) : "");
})
.join("\n");

// Brace languages share one tokenizer: comments, strings, keywords, the metric
// classes (orange, like metric names), numbers.
const braces = (keywords: string) => {
Expand Down Expand Up @@ -108,5 +146,91 @@ const rust = braces("use|fn|let|mut|for|in");
const java = braces("import|public|static|final|class|void|new|try|throws");
const csharp = braces("using|var|new|await");

// JavaScript and TypeScript application code: all three string quotes, no metric classes.
const javascript = (code: string) =>
tokenize(code, [
[/\/\/.*/y, (m) => span("comment", m[0])],
[/"[^"\n]*"|'[^'\n]*'|`[^`]*`/y, (m) => span("labels", m[0])],
[/\b(?:import|from|export|const|let|new|async|await|return|if|else|function|type|require|undefined|true|false)\b/y, (m) => span("fn", m[0])],
[/\b\d+(?:\.\d+)?\b/y, (m) => span("value", m[0])],
[/[a-zA-Z_$][\w$]*/y, (m) => escape(m[0])],
]);

// JSON and NDJSON log lines. OpenTelemetry's trace-context fields get their own
// class so a guide can color them like the traces in its diagrams.
const json = (code: string) =>
tokenize(code, [
[/"(?:trace_id|span_id|trace_flags)":\s*"[^"\n]*"/y, (m) => span("trace", m[0])],
[/"(?:[^"\\\n]|\\.)*"(?=\s*:)/y, (m) => span("name", m[0])],
[/"(?:[^"\\\n]|\\.)*"/y, (m) => span("labels", m[0])],
[/-?\b\d+(?:\.\d+)?\b|\b(?:true|false|null)\b/y, (m) => span("value", m[0])],
]);

// Python logging code. Loguru, the standard logging module and the OpenTelemetry
// SDK each get a class, so a guide can color them like the systems in its diagrams.
const pythonLogging = (code: string) =>
tokenize(code, [
[/#.*/y, (m) => span("comment", m[0])],
[/"[^"\n]*"|'[^'\n]*'/y, (m) => span("labels", m[0])],
[/\b(?:from|import|def|class|with|as|return|try|except|if|else|for|while|in|not|is|and|or|None|True|False|lambda|global|async|await|raise)\b/y, (m) => span("fn", m[0])],
[/\b(?:loguru|logger)\b/y, (m) => span("loguru", m[0])],
[/\b(?:logging|LogRecord|Handler|getLogger|basicConfig|makeRecord)\b/y, (m) => span("stdlib", m[0])],
[/\b(?:opentelemetry|LoggingHandler|LoggerProvider|logger_provider|set_logger_provider|trace|tracer|BatchLogRecordProcessor|SimpleLogRecordProcessor|OTLPLogExporter|ConsoleLogRecordExporter)\b/y, (m) => span("otel", m[0])],
[/\b\d+(?:\.\d+)?\b/y, (m) => span("value", m[0])],
[/[a-zA-Z_]\w*/y, (m) => escape(m[0])],
]);

// Loguru's default console line: time | LEVEL | location - message. Each level
// gets its own class (tok-level-info, tok-level-error...); traceback lines pass through.
const loguruOutput = (code: string) =>
code
.split("\n")
.map((line) => {
const entry = /^([\d-]+ [\d:.]+|[\d:]+)( \| )([A-Z]+)(\s*)( \| )(.*)$/.exec(line);
if (entry) {
const [, time, sep1, level, pad, sep2, rest] = entry;
const location = /^(\S+ - )(.*)$/.exec(rest);
return (
span("comment", time) +
span("comment", sep1) +
span(`level-${level.toLowerCase()}`, level) +
pad +
span("comment", sep2) +
(location ? span("comment", location[1]) + escape(location[2]) : escape(rest))
);
}
const error = /^(\w+(?:Error|Exception)):(.*)$/.exec(line);
if (error) return span("level-error", error[1]) + escape(":" + error[2]);
return /^\s*[│└]/.test(line) ? span("comment", line) : escape(line);
})
.join("\n");

// Shell commands: the program name, flags and quoted arguments.
const shell = (code: string) =>
tokenize(code, [
[/#.*/y, (m) => span("comment", m[0])],
[/(?<=^|\n|\| )(\s*)([\w.-]+)/y, (m) => escape(m[1]) + span("fn", m[2])],
[/"[^"\n]*"/y, (m) => span("labels", m[0])],
[/(?<=\s)--?[\w-]+/y, (m) => span("value", m[0])],
]);

export const highlight = (code: string, lang: GuideLang) =>
({ exposition, promql, yaml, python, typescript, go, rust, java, csharp, markdown })[lang](code);
({
exposition,
promql,
nrql,
yaml,
console: consoleSession,
python,
typescript,
go,
rust,
java,
csharp,
markdown,
javascript,
json,
shell,
"python-logging": pythonLogging,
"loguru-output": loguruOutput,
})[lang](code);
7 changes: 7 additions & 0 deletions apps/landing/src/pages/guides/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,13 @@ const investigationGuides = [
description: "Measure the right latency clock, isolate the slow requests, and trace a regression to the release and operation responsible.",
read: "14 min",
},
{
href: "/guides/python-logging-levels",
label: "Python",
title: "Python logging levels and how to set them",
description: "The five levels and when to use each, why info() prints nothing by default, and how to set the level with basicConfig, a --log-level flag or LOG_LEVEL.",
read: "6 min",
},
{
href: "/guides/incident-response",
label: "On-call",
Expand Down
Loading
Loading