diff --git a/.markdownlint.json b/.markdownlint.json index ab69aae..2b1559c 100644 --- a/.markdownlint.json +++ b/.markdownlint.json @@ -1,7 +1,5 @@ { - "MD013": { - "line_length": 160 - }, + "MD013": false, "MD033": false, "MD041": false } \ No newline at end of file diff --git a/src/guidelines/checklist/README.md b/src/guidelines/checklist/README.md index 88366ab..45683a2 100644 --- a/src/guidelines/checklist/README.md +++ b/src/guidelines/checklist/README.md @@ -14,6 +14,7 @@ - [ ] Panic Means 'Stop the Program' ([M-PANIC-IS-STOP]) - [ ] Detected Programming Bugs are Panics, Not Errors ([M-PANIC-ON-BUG]) - [ ] All Magic Values and Behaviors are Documented ([M-DOCUMENTED-MAGIC]) + - [ ] Use Structured Logging with Message Templates ([M-LOG-STRUCTURED]) - **Library / Interoperability** - [ ] Types are Send ([M-TYPES-SEND]) - [ ] Native Escape Hatches ([M-ESCAPE-HATCHES]) @@ -73,6 +74,7 @@ [M-PANIC-IS-STOP]: ../universal/#M-PANIC-IS-STOP [M-PANIC-ON-BUG]: ../universal/#M-PANIC-ON-BUG [M-DOCUMENTED-MAGIC]: ../universal/#M-DOCUMENTED-MAGIC +[M-LOG-STRUCTURED]: ../universal/#M-LOG-STRUCTURED [M-TYPES-SEND]: ../libs/interop/#M-TYPES-SEND diff --git a/src/guidelines/universal/M-LOG-STRUCTURED.md b/src/guidelines/universal/M-LOG-STRUCTURED.md new file mode 100644 index 0000000..ce1efde --- /dev/null +++ b/src/guidelines/universal/M-LOG-STRUCTURED.md @@ -0,0 +1,116 @@ + + +## Use Structured Logging with Message Templates (M-LOG-STRUCTURED) { #M-LOG-STRUCTURED } + +To minimize the cost of logging and to improve filtering capabilities. +0.1 + +Logging should use structured events with named properties and message templates following +the [message templates](https://messagetemplates.org/) specification. + +> **Note:** Examples use the [`tracing`](https://docs.rs/tracing/) crate's `event!` macro, +but these principles apply to any logging API that supports structured logging (e.g., `log`, +`slog`, custom telemetry systems). + +### Avoid String Formatting + +String formatting allocates memory at runtime. Message templates defer formatting until viewing time. +We recommend that message template includes all named properties for easier inspection at viewing time. + +```rust,ignore +// Bad: String formatting causes allocations +tracing::info!("file opened: {}", path); +tracing::info!(format!("file opened: {}", path)); + +// Good: Message templates with named properties +event!( + name: "file.open.success", + Level::INFO, + file.path = path.display(), + "file opened: {{file.path}}", +); +``` + +> **Note**: Use `{{property}}` the syntax in message templates which preserves the literal text +> while escaping Rust's format syntax. String formatting is deferred until logs are viewed. + +### Name Your Events + +Use hierarchical dot-notation: `..` + +```rust,ignore +// Bad: Unnamed events +event!( + Level::INFO, + file.path = file_path, + "file {{file.path}} processed succesfully", +); + +// Good: Named events +event!( + name: "file.processing.success", // event identifier + Level::INFO, + file.path = file_path, + "file {{file.path}} processed succesfully", +); +``` + +Named events enable grouping and filtering across log entries. + +### Follow OpenTelemetry Semantic Conventions + +Use [OTel semantic conventions](https://opentelemetry.io/docs/specs/semconv/) for common attributes if needed. +This enables standardization and interoperability. + +```rust,ignore +event!( + name: "file.write.success", + Level::INFO, + file.path = path.display(), // Standard OTel name + file.size = bytes_written, // Standard OTel name + file.directory = dir_path, // Standard OTel name + file.extension = extension, // Standard OTel name + file.operation = "write", // Custom name + "{{file.operation}} {{file.size}} bytes to {{file.path}} in {{file.directory}} extension={{file.extension}}", +); +``` + +Common conventions: + +- HTTP: `http.request.method`, `http.response.status_code`, `url.scheme`, `url.path`, `server.address` +- File: `file.path`, `file.directory`, `file.name`, `file.extension`, `file.size` +- Database: `db.system.name`, `db.namespace`, `db.operation.name`, `db.query.text` +- Errors: `error.type`, `error.message`, `exception.type`, `exception.stacktrace` + +### Redact Sensitive Data + +Do not log plain sensitive data as this might lead to privacy and security incidents. + +```rust,ignore +// Bad: Logs potentially sensitive data +event!( + name: "file.operation.started", + Level::INFO, + user.email = user.email, // Sensitive data + file.name = "license.txt", + "reading file {{file.name}} for user {{user.email}}", +); + +// Good: Redact sensitive parts +event!( + name: "file.operation.started", + Level::INFO, + user.email.redacted = redact_email(user.email), + file.name = "license.txt", + "reading file {{file.name}} for user {{user.email.redacted}}", +); +``` + +Sensitive data include user email, file paths revealing user identity, filenames containing secrets or tokens, +file contents with PII, temporary file paths with session IDs and more. Consider using the [`data_privacy`](https://crates.io/crates/data_privacy) crate for consistent redaction. + +### Further Reading + +- [Message Templates Specification](https://messagetemplates.org/) +- [OpenTelemetry Semantic Conventions](https://opentelemetry.io/docs/specs/semconv/) +- [OWASP Logging Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html) diff --git a/src/guidelines/universal/M-STATIC-VERIFICATION.md b/src/guidelines/universal/M-STATIC-VERIFICATION.md index 5328880..4ae45a8 100644 --- a/src/guidelines/universal/M-STATIC-VERIFICATION.md +++ b/src/guidelines/universal/M-STATIC-VERIFICATION.md @@ -73,6 +73,9 @@ unnecessary_safety_doc = "warn" unneeded_field_pattern = "warn" unused_result_ok = "warn" +# May cause issues with structured logging otherwise. +literal_string_with_formatting_args = "allow" + # Define custom opt outs here # ... ``` diff --git a/src/guidelines/universal/README.md b/src/guidelines/universal/README.md index 72f9048..3f464a1 100644 --- a/src/guidelines/universal/README.md +++ b/src/guidelines/universal/README.md @@ -13,3 +13,4 @@ {{#include M-PANIC-IS-STOP.md}} {{#include M-PANIC-ON-BUG.md}} {{#include M-DOCUMENTED-MAGIC.md}} +{{#include M-LOG-STRUCTURED.md}}