mirror of
https://github.com/SikongJueluo/cc-utils.git
synced 2025-11-29 12:57:50 +08:00
342 lines
10 KiB
Markdown
342 lines
10 KiB
Markdown
# ccStructLog
|
|
|
|
A modern, structured logging library for CC:Tweaked, inspired by Python's structlog. This library provides a flexible, extensible logging framework based on processors, renderers, and streams.
|
|
|
|
## Features
|
|
|
|
- **Structured Logging**: Log events are represented as key-value pairs, not just strings.
|
|
- **Extensible**: Easy to customize with processors, renderers, and streams.
|
|
- **Type Safe**: Full TypeScript support with proper type definitions.
|
|
- **CC:Tweaked Optimized**: Designed specifically for Minecraft's ComputerCraft environment, with features like file rotation and colored console output.
|
|
|
|
## Quick Start
|
|
|
|
The easiest way to get started is to create a `Logger` instance and configure it with processors, a renderer, and streams.
|
|
|
|
Here's a simple example of a logger that prints colored, human-readable messages to the console:
|
|
|
|
```typescript
|
|
import {
|
|
Logger,
|
|
LogLevel,
|
|
processor,
|
|
textRenderer,
|
|
ConsoleStream,
|
|
} from "@/lib/ccStructLog";
|
|
|
|
// Create a logger
|
|
const logger = new Logger({
|
|
processors: [
|
|
processor.addTimestamp({ format: "%T" }), // Add HH:MM:SS timestamp
|
|
processor.filterByLevel(LogLevel.Info), // Log Info and higher
|
|
processor.addSource("MyApp"),
|
|
],
|
|
renderer: textRenderer,
|
|
streams: [new ConsoleStream()],
|
|
});
|
|
|
|
// Log messages with context
|
|
logger.info("Server started", { port: 8080, version: "1.0.0" });
|
|
logger.warn("Low disk space", { available: 1024, threshold: 2048 });
|
|
logger.error("Connection failed", { host: "example.com", retries: 3 });
|
|
|
|
// This debug message will be filtered out by `filterByLevel`
|
|
logger.debug("This is a debug message.");
|
|
```
|
|
|
|
## Core Concepts
|
|
|
|
### Log Levels
|
|
|
|
```typescript
|
|
export enum LogLevel {
|
|
Trace = 0, // Very detailed diagnostic information
|
|
Debug = 1, // Diagnostic information for development
|
|
Info = 2, // General informational messages
|
|
Warn = 3, // Potentially harmful situations
|
|
Error = 4, // Error events that might allow continued execution
|
|
Fatal = 5, // Very severe errors that might cause termination
|
|
}
|
|
```
|
|
|
|
### Data Flow
|
|
|
|
1. **Capture**: User calls `logger.info("message", {key: "value"})`.
|
|
2. **Package**: A `LogEvent` object (`Map<string, unknown>`) is created with the message, context, and metadata.
|
|
3. **Process**: The event is passed through a chain of processors (e.g., to add a timestamp, filter by level).
|
|
4. **Render**: The final event is converted to a string by a renderer (e.g., `textRenderer`, `jsonRenderer`).
|
|
5. **Output**: The string is sent to one or more streams (e.g., console, file).
|
|
|
|
## Common Configurations
|
|
|
|
### Development Logger
|
|
A typical development logger is configured for human-readable console output with timestamps and colors.
|
|
|
|
```typescript
|
|
import {
|
|
Logger,
|
|
processor,
|
|
textRenderer,
|
|
ConsoleStream,
|
|
} from "@/lib/ccStructLog";
|
|
|
|
const devLogger = new Logger({
|
|
processors: [
|
|
processor.addTimestamp({ format: "%F %T" }), // YYYY-MM-DD HH:MM:SS
|
|
processor.addSource("DevApp"),
|
|
processor.addComputerId(),
|
|
],
|
|
renderer: textRenderer,
|
|
streams: [new ConsoleStream()],
|
|
});
|
|
|
|
devLogger.debug("This is a debug message.", { user: "dev" });
|
|
```
|
|
|
|
### Production Logger
|
|
A production logger is often configured to write machine-readable JSON logs to a file with daily rotation.
|
|
|
|
```typescript
|
|
import {
|
|
Logger,
|
|
LogLevel,
|
|
processor,
|
|
jsonRenderer,
|
|
FileStream,
|
|
DAY,
|
|
} from "@/lib/ccStructLog";
|
|
|
|
const prodLogger = new Logger({
|
|
processors: [
|
|
processor.addTimestamp(), // Default format is %F %T
|
|
processor.filterByLevel(LogLevel.Info),
|
|
processor.addSource("ProdApp"),
|
|
processor.addComputerId(),
|
|
],
|
|
renderer: jsonRenderer,
|
|
streams: [
|
|
new FileStream("app.log", DAY), // Rotate daily
|
|
],
|
|
});
|
|
|
|
prodLogger.info("Application is running in production.");
|
|
```
|
|
|
|
## Custom Configuration
|
|
|
|
You can create a logger with any combination of processors, renderers, and streams.
|
|
|
|
```typescript
|
|
import {
|
|
Logger,
|
|
processor,
|
|
jsonRenderer,
|
|
FileStream,
|
|
ConsoleStream,
|
|
HOUR,
|
|
} from "@/lib/ccStructLog";
|
|
|
|
const logger = new Logger({
|
|
processors: [
|
|
processor.addTimestamp(),
|
|
processor.addComputerId(),
|
|
processor.addSource("MyApplication"),
|
|
],
|
|
renderer: jsonRenderer,
|
|
streams: [
|
|
new ConsoleStream(),
|
|
new FileStream("custom.log", HOUR), // Rotate every hour
|
|
],
|
|
});
|
|
|
|
logger.info("Custom logger reporting for duty.", { user: "admin" });
|
|
```
|
|
|
|
## Processors
|
|
|
|
Processors are functions that modify, enrich, or filter log events before they are rendered. They are all available under the `processor` namespace.
|
|
|
|
### Built-in Processors
|
|
```typescript
|
|
import { Logger, LogLevel, processor } from "@/lib/ccStructLog";
|
|
|
|
// Usage example
|
|
const logger = new Logger({
|
|
processors: [
|
|
// Adds a timestamp. Format is compatible with os.date().
|
|
// Default: "%F %T" (e.g., "2023-10-27 15:30:00")
|
|
processor.addTimestamp({ format: "%T" }), // e.g., "15:30:00"
|
|
|
|
// Filter by minimum level
|
|
processor.filterByLevel(LogLevel.Warn), // Only allow Warn, Error, Fatal
|
|
|
|
// Filter based on a custom predicate
|
|
processor.filterBy((event) => event.get("user") === "admin"),
|
|
|
|
// Add source/logger name
|
|
processor.addSource("MyApp"),
|
|
|
|
// Add computer ID or label
|
|
processor.addComputerId(),
|
|
processor.addComputerLabel(),
|
|
|
|
// Add static fields to all events
|
|
processor.addStaticFields({ env: "production", version: "1.2.3" }),
|
|
|
|
// Transform a specific field's value
|
|
processor.transformField("user_id", (id) => `user_${id}`),
|
|
|
|
// Remove sensitive fields
|
|
processor.removeFields(["password", "token"]),
|
|
],
|
|
// ... other config
|
|
});
|
|
```
|
|
|
|
### Custom Processors
|
|
A custom processor is a function that takes a `LogEvent` and returns a `LogEvent` or `undefined` (to drop the event).
|
|
|
|
```typescript
|
|
import { LogEvent } from "@/lib/ccStructLog";
|
|
|
|
// Add a unique request ID to all log events
|
|
const addRequestId = (event: LogEvent): LogEvent => {
|
|
event.set("requestId", `req_${Math.random().toString(36).substr(2, 9)}`);
|
|
return event;
|
|
};
|
|
|
|
// Sanitize sensitive information
|
|
const sanitizePasswords = (event: LogEvent): LogEvent => {
|
|
if (event.has("password")) {
|
|
event.set("password", "[REDACTED]");
|
|
}
|
|
return event;
|
|
};
|
|
```
|
|
|
|
## Renderers
|
|
|
|
Renderers convert the final `LogEvent` object into a string.
|
|
|
|
### Built-in Renderers
|
|
```typescript
|
|
import { textRenderer, jsonRenderer } from "@/lib/ccStructLog";
|
|
|
|
// textRenderer: Human-readable, colored output for the console.
|
|
// Example: [15:30:45] [INFO] Message key=value
|
|
|
|
// jsonRenderer: Machine-readable JSON output.
|
|
// Example: {"level":2,"message":"Message","key":"value","timestamp":"15:30:45"}
|
|
```
|
|
|
|
## Streams
|
|
|
|
Streams handle the final output destination. You can use multiple streams to send logs to different places.
|
|
|
|
### Built-in Streams
|
|
```typescript
|
|
import {
|
|
ConsoleStream,
|
|
FileStream,
|
|
BufferStream,
|
|
NullStream,
|
|
ConditionalStream,
|
|
LogLevel,
|
|
DAY,
|
|
} from "@/lib/ccStructLog";
|
|
|
|
// File stream with daily rotation
|
|
const fileStream = new FileStream("app.log", DAY);
|
|
|
|
// Buffer stream (useful for testing or UI display)
|
|
const bufferStream = new BufferStream(100); // Keep last 100 messages
|
|
|
|
// Conditional stream (only send errors to a separate file)
|
|
const errorStream = new ConditionalStream(
|
|
new FileStream("errors.log"),
|
|
(message, event) => (event.get("level") as LogLevel) >= LogLevel.Error
|
|
);
|
|
```
|
|
|
|
## File Rotation
|
|
|
|
`FileStream` supports automatic file rotation based on time intervals. The rotation interval is specified in seconds as the second argument to the constructor.
|
|
|
|
```typescript
|
|
import { FileStream, HOUR, DAY, WEEK } from "@/lib/ccStructLog";
|
|
|
|
// Rotate every hour
|
|
const hourlyLog = new FileStream("app_hourly.log", HOUR);
|
|
|
|
// Rotate daily (recommended for most applications)
|
|
const dailyLog = new FileStream("app_daily.log", DAY);
|
|
|
|
// Rotate weekly
|
|
const weeklyLog = new FileStream("app_weekly.log", WEEK);
|
|
|
|
// No rotation (pass 0 or undefined)
|
|
const permanentLog = new FileStream("permanent.log", 0);
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
1. **Use Structured Context**: Always provide relevant context as key-value pairs.
|
|
```typescript
|
|
// Good
|
|
logger.info("User action completed", { userId: 123, action: "purchase" });
|
|
|
|
// Less useful
|
|
logger.info("User 123 purchased an item");
|
|
```
|
|
|
|
2. **Choose Appropriate Levels**:
|
|
- `debug`: For developers to diagnose issues.
|
|
- `info`: Normal application behavior.
|
|
- `warn`: Potentially harmful situations that don't break functionality.
|
|
- `error`: Errors that affect a single operation but not the whole app.
|
|
- `fatal`: Critical errors that require the application to shut down.
|
|
|
|
3. **Use a `source`**: Identify which component generated the log using `processor.addSource`.
|
|
```typescript
|
|
import { Logger, processor } from "@/lib/ccStructLog";
|
|
|
|
const logger = new Logger({
|
|
processors: [processor.addSource("UserService")],
|
|
// ...
|
|
});
|
|
```
|
|
|
|
4. **Sanitize Sensitive Data**: Use a processor to remove passwords, API keys, etc.
|
|
```typescript
|
|
import { Logger, processor } from "@/lib/ccStructLog";
|
|
|
|
const secureLogger = new Logger({
|
|
processors: [ processor.removeFields(["password", "token"]) ],
|
|
//...
|
|
});
|
|
```
|
|
|
|
5. **Proper Cleanup**: Close loggers during application shutdown to ensure file streams are saved.
|
|
```typescript
|
|
// At application shutdown
|
|
logger.close();
|
|
```
|
|
|
|
## Examples
|
|
|
|
See `src/logExample/main.ts` for comprehensive usage examples including:
|
|
- Basic logging patterns
|
|
- Custom processor chains
|
|
- Multiple output streams with different formats
|
|
- Error handling strategies
|
|
|
|
## API Reference
|
|
|
|
For complete API documentation, refer to the TypeScript definitions in each module:
|
|
- `src/lib/ccStructLog/types.ts` - Core interfaces and types
|
|
- `src/lib/ccStructLog/Logger.ts` - Main Logger class
|
|
- `src/lib/ccStructLog/processors.ts` - Built-in processors
|
|
- `src/lib/ccStructLog/renderers.ts` - Built-in renderers
|
|
- `src/lib/ccStructLog/streams.ts` - Built-in streams
|
|
- `src/lib/ccStructLog/index.ts` - Convenience functions and exports
|