mirror of
https://dev.azure.com/tstanciu94/PhantomMind/_git/Bitip
synced 2025-10-13 01:52:19 +03:00
refactor: add logging configuration with adjustable log levels and update related documentation
This commit is contained in:
parent
66141c696b
commit
c546a3c1a0
1
.env
1
.env
@ -13,6 +13,7 @@ PORT=5172
|
||||
BASE_PATH=/
|
||||
NODE_ENV=development
|
||||
ENABLE_HTTPS_SECURITY=false
|
||||
LOG_LEVEL=debug
|
||||
|
||||
# Database Path - Local MaxMind databases location
|
||||
MAXMIND_DB_PATH=D:\\tools\\maxmind-dbs
|
||||
|
@ -15,6 +15,14 @@ PORT=5172
|
||||
BASE_PATH=/
|
||||
NODE_ENV=production
|
||||
|
||||
# Logging Configuration
|
||||
# Minimum log level: debug, info, warning, error
|
||||
# - debug: All logs (very verbose)
|
||||
# - info: Info, warnings, and errors (default for development)
|
||||
# - warning: Only warnings and errors (recommended for production)
|
||||
# - error: Only errors
|
||||
LOG_LEVEL=warning
|
||||
|
||||
# Database Path (usually doesn't need to change)
|
||||
MAXMIND_DB_PATH=/usr/share/maxmind
|
||||
|
||||
|
@ -208,8 +208,8 @@
|
||||
"**HTTPS Security Headers** - Toggle security headers (CSP, HSTS, COOP, CORP) based on deployment type (HTTP vs HTTPS)",
|
||||
"**Port Configuration** - Customize backend and frontend ports",
|
||||
"**Base Path Support** - Deploy under custom URL paths for reverse proxy scenarios",
|
||||
"**Logging Levels** - Adjust verbosity from debug to production",
|
||||
"**Seq Integration** - Optional structured logging to Seq server",
|
||||
"**Configurable Logging Levels** - Hierarchical log filtering (debug, info, warning, error) similar to Serilog in .NET",
|
||||
"**Seq Integration** - Optional structured logging to Seq server with configurable minimum level",
|
||||
"**Database Path** - Configurable GeoLite2 database location"
|
||||
]
|
||||
},
|
||||
@ -217,12 +217,14 @@
|
||||
"title": "Monitoring & Observability",
|
||||
"items": [
|
||||
"**Structured Logging** - JSON-formatted logs with context and correlation IDs",
|
||||
"**Hierarchical Log Levels** - Configurable minimum log level (debug, info, warning, error) for filtering",
|
||||
"**Version Endpoint** - Check deployed version, build date, and git revision",
|
||||
"**Health Checks** - Monitor service availability and database status",
|
||||
"**Error Tracking** - Detailed error messages with stack traces in development",
|
||||
"**Request Logging** - Log all API requests with IP, method, path, and response time",
|
||||
"**Rate Limit Metrics** - Track rate limit hits and blocked requests",
|
||||
"**Seq Integration** - Optional centralized logging with query and dashboard capabilities"
|
||||
"**Seq Integration** - Optional centralized logging with query and dashboard capabilities",
|
||||
"**Production-Ready Logging** - Minimal overhead with filtered log levels for production environments"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
62
README.md
62
README.md
@ -51,6 +51,7 @@ PORT=5172
|
||||
BASE_PATH=/
|
||||
NODE_ENV=development
|
||||
ENABLE_HTTPS_SECURITY=false
|
||||
LOG_LEVEL=debug # debug | info | warning | error
|
||||
|
||||
# Database Path - Point to your MaxMind databases location
|
||||
MAXMIND_DB_PATH=D:\tools\maxmind-dbs # Windows
|
||||
@ -98,13 +99,14 @@ This will start:
|
||||
|
||||
#### 1. Configure Environment
|
||||
|
||||
Edit `.env` file with your MaxMind credentials:
|
||||
Edit `.env` file with your MaxMind credentials and logging level:
|
||||
|
||||
```env
|
||||
GEOIPUPDATE_ACCOUNT_ID=your_account_id_here
|
||||
GEOIPUPDATE_LICENSE_KEY=your_license_key_here
|
||||
FRONTEND_API_KEY=your-secure-frontend-api-key-here
|
||||
EXTERNAL_API_KEYS=your-secure-external-api-key-1,your-secure-external-api-key-2
|
||||
LOG_LEVEL=warning # Recommended for production (debug | info | warning | error)
|
||||
```
|
||||
|
||||
#### 2. Start Services
|
||||
@ -113,6 +115,28 @@ EXTERNAL_API_KEYS=your-secure-external-api-key-1,your-secure-external-api-key-2
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
**Or with Docker run:**
|
||||
|
||||
```bash
|
||||
# Build image
|
||||
docker build -t bitip-geoip:latest .
|
||||
|
||||
# Run with custom log level
|
||||
docker run -d \
|
||||
-p 5172:5172 \
|
||||
-e LOG_LEVEL=warning \
|
||||
-e FRONTEND_API_KEY=your-api-key \
|
||||
-v /path/to/maxmind-dbs:/usr/share/maxmind \
|
||||
bitip-geoip:latest
|
||||
```
|
||||
|
||||
**Available log levels:**
|
||||
|
||||
- `debug` - All logs (verbose, for development/troubleshooting)
|
||||
- `info` - Info + warnings + errors (default)
|
||||
- `warning` - Warnings + errors only (recommended for production)
|
||||
- `error` - Errors only (minimal logging)
|
||||
|
||||
#### 3. Access the Service
|
||||
|
||||
- **Web Interface**: http://localhost:5172
|
||||
@ -323,24 +347,24 @@ npm run install:all # Install all dependencies (root + backend + frontend)
|
||||
|
||||
### Environment Variables
|
||||
|
||||
| Variable | Default | Description |
|
||||
| -------------------------- | ---------------------- | --------------------------------------------------------------- |
|
||||
| `PORT` | `5172` | Server port |
|
||||
| `BASE_PATH` | `/` | Application base path |
|
||||
| `NODE_ENV` | `development` | Environment mode (development/production) |
|
||||
| `ENABLE_HTTPS_SECURITY` | `false` | Enable HTTPS security headers (CSP, HSTS, COOP, CORP) |
|
||||
| `MAXMIND_DB_PATH` | `/usr/share/maxmind` | MaxMind database directory |
|
||||
| `FRONTEND_API_KEY` | `frontend-default-key` | Frontend API key |
|
||||
| `EXTERNAL_API_KEYS` | `external-default-key` | External API keys (comma-separated) |
|
||||
| `FRONTEND_ALLOWED_ORIGINS` | `http://localhost:*` | Allowed CORS origins for frontend (comma-separated) |
|
||||
| `FRONTEND_RATE_WINDOW_MS` | `60000` | Frontend rate limit window (milliseconds) |
|
||||
| `FRONTEND_RATE_MAX` | `30` | Frontend rate limit max requests |
|
||||
| `EXTERNAL_RATE_WINDOW_MS` | `60000` | External rate limit window (milliseconds) |
|
||||
| `EXTERNAL_RATE_MAX` | `1000` | External rate limit max requests |
|
||||
| `BATCH_LIMIT` | `100` | Maximum IPs per batch request |
|
||||
| `DEBOUNCE_MS` | `2000` | Frontend input debounce delay (milliseconds) |
|
||||
| `SEQ_URL` | - | Seq logging server URL (optional) |
|
||||
| `SEQ_API_KEY` | - | Seq API key (optional) |
|
||||
| Variable | Default | Description |
|
||||
| -------------------------- | ---------------------- | ----------------------------------------------------- |
|
||||
| `PORT` | `5172` | Server port |
|
||||
| `BASE_PATH` | `/` | Application base path |
|
||||
| `NODE_ENV` | `development` | Environment mode (development/production) |
|
||||
| `ENABLE_HTTPS_SECURITY` | `false` | Enable HTTPS security headers (CSP, HSTS, COOP, CORP) |
|
||||
| `MAXMIND_DB_PATH` | `/usr/share/maxmind` | MaxMind database directory |
|
||||
| `FRONTEND_API_KEY` | `frontend-default-key` | Frontend API key |
|
||||
| `EXTERNAL_API_KEYS` | `external-default-key` | External API keys (comma-separated) |
|
||||
| `FRONTEND_ALLOWED_ORIGINS` | `http://localhost:*` | Allowed CORS origins for frontend (comma-separated) |
|
||||
| `FRONTEND_RATE_WINDOW_MS` | `60000` | Frontend rate limit window (milliseconds) |
|
||||
| `FRONTEND_RATE_MAX` | `30` | Frontend rate limit max requests |
|
||||
| `EXTERNAL_RATE_WINDOW_MS` | `60000` | External rate limit window (milliseconds) |
|
||||
| `EXTERNAL_RATE_MAX` | `1000` | External rate limit max requests |
|
||||
| `BATCH_LIMIT` | `100` | Maximum IPs per batch request |
|
||||
| `DEBOUNCE_MS` | `2000` | Frontend input debounce delay (milliseconds) |
|
||||
| `SEQ_URL` | - | Seq logging server URL (optional) |
|
||||
| `SEQ_API_KEY` | - | Seq API key (optional) |
|
||||
|
||||
### MaxMind Database Setup
|
||||
|
||||
|
@ -120,6 +120,25 @@ ENABLE_HTTPS_SECURITY=false
|
||||
- `production` - Optimized for performance, minimal logging
|
||||
- **Example**: `production`
|
||||
|
||||
**`LOG_LEVEL`**
|
||||
|
||||
- **Purpose**: Minimum logging level for both console and Seq output
|
||||
- **Format**: String
|
||||
- **Required**: No
|
||||
- **Default**: `info`
|
||||
- **Valid Values**:
|
||||
- `debug` - All logs including debug messages (very verbose, use only for troubleshooting)
|
||||
- `info` - Informational messages, warnings, and errors (recommended for development)
|
||||
- `warning` - Only warnings and errors (recommended for production)
|
||||
- `error` - Only error messages (minimal logging)
|
||||
- **Use Cases**:
|
||||
- Development: `debug` or `info` - Full visibility into application behavior
|
||||
- Production: `warning` or `error` - Reduce log volume and focus on issues
|
||||
- Troubleshooting: Temporarily set to `debug` to diagnose problems
|
||||
- **Example**: `warning`
|
||||
- **Note**: Similar to Serilog's minimum level filtering in .NET
|
||||
- **Impact**: Affects both console output and Seq structured logging
|
||||
|
||||
**`ENABLE_HTTPS_SECURITY`**
|
||||
|
||||
- **Purpose**: Enable/disable HTTPS security headers (Helmet middleware)
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Config } from '../types/index.js';
|
||||
import { Config, LogLevel, LOG_LEVELS } from '../types/index.js';
|
||||
import path from 'path';
|
||||
import { config as dotenvConfig } from 'dotenv';
|
||||
import { fileURLToPath } from 'url';
|
||||
@ -10,12 +10,22 @@ const __dirname = dirname(__filename);
|
||||
// Load .env file from root directory
|
||||
dotenvConfig({ path: path.join(__dirname, '../../../.env') });
|
||||
|
||||
// Parse log level from environment variable
|
||||
const parseLogLevel = (level?: string): LogLevel => {
|
||||
const normalized = (level || LOG_LEVELS.INFO).toLowerCase();
|
||||
const validLevels = Object.values(LOG_LEVELS);
|
||||
return validLevels.includes(normalized as LogLevel)
|
||||
? (normalized as LogLevel)
|
||||
: LOG_LEVELS.INFO;
|
||||
};
|
||||
|
||||
export const config: Config = {
|
||||
port: parseInt(process.env.PORT || '5172', 10),
|
||||
basePath: process.env.BASE_PATH || '/',
|
||||
maxmindDbPath: process.env.MAXMIND_DB_PATH || '/usr/share/maxmind',
|
||||
seqUrl: process.env.SEQ_URL,
|
||||
seqApiKey: process.env.SEQ_API_KEY,
|
||||
logLevel: parseLogLevel(process.env.LOG_LEVEL),
|
||||
enableHttpsSecurity: process.env.ENABLE_HTTPS_SECURITY === 'true',
|
||||
apiKeys: {
|
||||
frontend: process.env.FRONTEND_API_KEY || 'frontend-default-key',
|
||||
|
@ -96,7 +96,7 @@ class GeoIPService {
|
||||
const response: City = this.cityReader!.city(ip);
|
||||
const result: DetailedGeoIPResponse = {
|
||||
ip,
|
||||
location: response as any as GeoIPLocation,
|
||||
location: response as GeoIPLocation,
|
||||
};
|
||||
|
||||
this.cache.set(cacheKey, result);
|
||||
|
@ -1,10 +1,14 @@
|
||||
import { Logger as SeqLogger, SeqLoggerConfig } from 'seq-logging';
|
||||
import { LogLevel, LOG_LEVELS, LOG_LEVEL_PRIORITY } from '../types/index.js';
|
||||
import config from './config.js';
|
||||
|
||||
class Logger {
|
||||
private seqLogger?: SeqLogger;
|
||||
private minLevel: LogLevel;
|
||||
|
||||
constructor() {
|
||||
this.minLevel = config.logLevel;
|
||||
|
||||
if (config.seqUrl) {
|
||||
const seqConfig: SeqLoggerConfig = {
|
||||
serverUrl: config.seqUrl,
|
||||
@ -15,9 +19,20 @@ class Logger {
|
||||
};
|
||||
this.seqLogger = new SeqLogger(seqConfig);
|
||||
}
|
||||
|
||||
// Log initial configuration
|
||||
console.log(
|
||||
`[INFO] Logger initialized with minimum level: ${this.minLevel.toUpperCase()}`
|
||||
);
|
||||
}
|
||||
|
||||
info(message: string, properties?: any): void {
|
||||
private shouldLog(level: LogLevel): boolean {
|
||||
return LOG_LEVEL_PRIORITY[level] >= LOG_LEVEL_PRIORITY[this.minLevel];
|
||||
}
|
||||
|
||||
info(message: string, properties?: Record<string, unknown>): void {
|
||||
if (!this.shouldLog(LOG_LEVELS.INFO)) return;
|
||||
|
||||
console.log(`[INFO] ${message}`, properties || '');
|
||||
if (this.seqLogger) {
|
||||
this.seqLogger.emit({
|
||||
@ -29,7 +44,9 @@ class Logger {
|
||||
}
|
||||
}
|
||||
|
||||
warn(message: string, properties?: any): void {
|
||||
warn(message: string, properties?: Record<string, unknown>): void {
|
||||
if (!this.shouldLog(LOG_LEVELS.WARNING)) return;
|
||||
|
||||
console.warn(`[WARN] ${message}`, properties || '');
|
||||
if (this.seqLogger) {
|
||||
this.seqLogger.emit({
|
||||
@ -41,7 +58,13 @@ class Logger {
|
||||
}
|
||||
}
|
||||
|
||||
error(message: string, error?: Error, properties?: any): void {
|
||||
error(
|
||||
message: string,
|
||||
error?: Error,
|
||||
properties?: Record<string, unknown>
|
||||
): void {
|
||||
if (!this.shouldLog(LOG_LEVELS.ERROR)) return;
|
||||
|
||||
console.error(`[ERROR] ${message}`, error || '', properties || '');
|
||||
if (this.seqLogger) {
|
||||
this.seqLogger.emit({
|
||||
@ -57,7 +80,9 @@ class Logger {
|
||||
}
|
||||
}
|
||||
|
||||
debug(message: string, properties?: any): void {
|
||||
debug(message: string, properties?: Record<string, unknown>): void {
|
||||
if (!this.shouldLog(LOG_LEVELS.DEBUG)) return;
|
||||
|
||||
console.debug(`[DEBUG] ${message}`, properties || '');
|
||||
if (this.seqLogger) {
|
||||
this.seqLogger.emit({
|
||||
|
@ -1,3 +1,7 @@
|
||||
import { LogLevel } from './log';
|
||||
|
||||
export * from './log';
|
||||
|
||||
export interface GeoIPLocation {
|
||||
country?: {
|
||||
iso_code?: string;
|
||||
@ -69,6 +73,7 @@ export interface Config {
|
||||
maxmindDbPath: string;
|
||||
seqUrl?: string;
|
||||
seqApiKey?: string;
|
||||
logLevel: LogLevel;
|
||||
enableHttpsSecurity: boolean;
|
||||
apiKeys: {
|
||||
frontend: string;
|
||||
|
15
src/backend/types/log.ts
Normal file
15
src/backend/types/log.ts
Normal file
@ -0,0 +1,15 @@
|
||||
export const LOG_LEVELS = {
|
||||
DEBUG: 'debug',
|
||||
INFO: 'info',
|
||||
WARNING: 'warning',
|
||||
ERROR: 'error',
|
||||
} as const;
|
||||
|
||||
export type LogLevel = (typeof LOG_LEVELS)[keyof typeof LOG_LEVELS];
|
||||
|
||||
export const LOG_LEVEL_PRIORITY: Record<LogLevel, number> = {
|
||||
[LOG_LEVELS.DEBUG]: 0,
|
||||
[LOG_LEVELS.INFO]: 1,
|
||||
[LOG_LEVELS.WARNING]: 2,
|
||||
[LOG_LEVELS.ERROR]: 3,
|
||||
} as const;
|
Loading…
x
Reference in New Issue
Block a user