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}}