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=/
|
BASE_PATH=/
|
||||||
NODE_ENV=development
|
NODE_ENV=development
|
||||||
ENABLE_HTTPS_SECURITY=false
|
ENABLE_HTTPS_SECURITY=false
|
||||||
|
LOG_LEVEL=debug
|
||||||
|
|
||||||
# Database Path - Local MaxMind databases location
|
# Database Path - Local MaxMind databases location
|
||||||
MAXMIND_DB_PATH=D:\\tools\\maxmind-dbs
|
MAXMIND_DB_PATH=D:\\tools\\maxmind-dbs
|
||||||
|
@ -15,6 +15,14 @@ PORT=5172
|
|||||||
BASE_PATH=/
|
BASE_PATH=/
|
||||||
NODE_ENV=production
|
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)
|
# Database Path (usually doesn't need to change)
|
||||||
MAXMIND_DB_PATH=/usr/share/maxmind
|
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)",
|
"**HTTPS Security Headers** - Toggle security headers (CSP, HSTS, COOP, CORP) based on deployment type (HTTP vs HTTPS)",
|
||||||
"**Port Configuration** - Customize backend and frontend ports",
|
"**Port Configuration** - Customize backend and frontend ports",
|
||||||
"**Base Path Support** - Deploy under custom URL paths for reverse proxy scenarios",
|
"**Base Path Support** - Deploy under custom URL paths for reverse proxy scenarios",
|
||||||
"**Logging Levels** - Adjust verbosity from debug to production",
|
"**Configurable Logging Levels** - Hierarchical log filtering (debug, info, warning, error) similar to Serilog in .NET",
|
||||||
"**Seq Integration** - Optional structured logging to Seq server",
|
"**Seq Integration** - Optional structured logging to Seq server with configurable minimum level",
|
||||||
"**Database Path** - Configurable GeoLite2 database location"
|
"**Database Path** - Configurable GeoLite2 database location"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -217,12 +217,14 @@
|
|||||||
"title": "Monitoring & Observability",
|
"title": "Monitoring & Observability",
|
||||||
"items": [
|
"items": [
|
||||||
"**Structured Logging** - JSON-formatted logs with context and correlation IDs",
|
"**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",
|
"**Version Endpoint** - Check deployed version, build date, and git revision",
|
||||||
"**Health Checks** - Monitor service availability and database status",
|
"**Health Checks** - Monitor service availability and database status",
|
||||||
"**Error Tracking** - Detailed error messages with stack traces in development",
|
"**Error Tracking** - Detailed error messages with stack traces in development",
|
||||||
"**Request Logging** - Log all API requests with IP, method, path, and response time",
|
"**Request Logging** - Log all API requests with IP, method, path, and response time",
|
||||||
"**Rate Limit Metrics** - Track rate limit hits and blocked requests",
|
"**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=/
|
BASE_PATH=/
|
||||||
NODE_ENV=development
|
NODE_ENV=development
|
||||||
ENABLE_HTTPS_SECURITY=false
|
ENABLE_HTTPS_SECURITY=false
|
||||||
|
LOG_LEVEL=debug # debug | info | warning | error
|
||||||
|
|
||||||
# Database Path - Point to your MaxMind databases location
|
# Database Path - Point to your MaxMind databases location
|
||||||
MAXMIND_DB_PATH=D:\tools\maxmind-dbs # Windows
|
MAXMIND_DB_PATH=D:\tools\maxmind-dbs # Windows
|
||||||
@ -98,13 +99,14 @@ This will start:
|
|||||||
|
|
||||||
#### 1. Configure Environment
|
#### 1. Configure Environment
|
||||||
|
|
||||||
Edit `.env` file with your MaxMind credentials:
|
Edit `.env` file with your MaxMind credentials and logging level:
|
||||||
|
|
||||||
```env
|
```env
|
||||||
GEOIPUPDATE_ACCOUNT_ID=your_account_id_here
|
GEOIPUPDATE_ACCOUNT_ID=your_account_id_here
|
||||||
GEOIPUPDATE_LICENSE_KEY=your_license_key_here
|
GEOIPUPDATE_LICENSE_KEY=your_license_key_here
|
||||||
FRONTEND_API_KEY=your-secure-frontend-api-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
|
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
|
#### 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
|
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
|
#### 3. Access the Service
|
||||||
|
|
||||||
- **Web Interface**: http://localhost:5172
|
- **Web Interface**: http://localhost:5172
|
||||||
@ -323,24 +347,24 @@ npm run install:all # Install all dependencies (root + backend + frontend)
|
|||||||
|
|
||||||
### Environment Variables
|
### Environment Variables
|
||||||
|
|
||||||
| Variable | Default | Description |
|
| Variable | Default | Description |
|
||||||
| -------------------------- | ---------------------- | --------------------------------------------------------------- |
|
| -------------------------- | ---------------------- | ----------------------------------------------------- |
|
||||||
| `PORT` | `5172` | Server port |
|
| `PORT` | `5172` | Server port |
|
||||||
| `BASE_PATH` | `/` | Application base path |
|
| `BASE_PATH` | `/` | Application base path |
|
||||||
| `NODE_ENV` | `development` | Environment mode (development/production) |
|
| `NODE_ENV` | `development` | Environment mode (development/production) |
|
||||||
| `ENABLE_HTTPS_SECURITY` | `false` | Enable HTTPS security headers (CSP, HSTS, COOP, CORP) |
|
| `ENABLE_HTTPS_SECURITY` | `false` | Enable HTTPS security headers (CSP, HSTS, COOP, CORP) |
|
||||||
| `MAXMIND_DB_PATH` | `/usr/share/maxmind` | MaxMind database directory |
|
| `MAXMIND_DB_PATH` | `/usr/share/maxmind` | MaxMind database directory |
|
||||||
| `FRONTEND_API_KEY` | `frontend-default-key` | Frontend API key |
|
| `FRONTEND_API_KEY` | `frontend-default-key` | Frontend API key |
|
||||||
| `EXTERNAL_API_KEYS` | `external-default-key` | External API keys (comma-separated) |
|
| `EXTERNAL_API_KEYS` | `external-default-key` | External API keys (comma-separated) |
|
||||||
| `FRONTEND_ALLOWED_ORIGINS` | `http://localhost:*` | Allowed CORS origins for frontend (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_WINDOW_MS` | `60000` | Frontend rate limit window (milliseconds) |
|
||||||
| `FRONTEND_RATE_MAX` | `30` | Frontend rate limit max requests |
|
| `FRONTEND_RATE_MAX` | `30` | Frontend rate limit max requests |
|
||||||
| `EXTERNAL_RATE_WINDOW_MS` | `60000` | External rate limit window (milliseconds) |
|
| `EXTERNAL_RATE_WINDOW_MS` | `60000` | External rate limit window (milliseconds) |
|
||||||
| `EXTERNAL_RATE_MAX` | `1000` | External rate limit max requests |
|
| `EXTERNAL_RATE_MAX` | `1000` | External rate limit max requests |
|
||||||
| `BATCH_LIMIT` | `100` | Maximum IPs per batch request |
|
| `BATCH_LIMIT` | `100` | Maximum IPs per batch request |
|
||||||
| `DEBOUNCE_MS` | `2000` | Frontend input debounce delay (milliseconds) |
|
| `DEBOUNCE_MS` | `2000` | Frontend input debounce delay (milliseconds) |
|
||||||
| `SEQ_URL` | - | Seq logging server URL (optional) |
|
| `SEQ_URL` | - | Seq logging server URL (optional) |
|
||||||
| `SEQ_API_KEY` | - | Seq API key (optional) |
|
| `SEQ_API_KEY` | - | Seq API key (optional) |
|
||||||
|
|
||||||
### MaxMind Database Setup
|
### MaxMind Database Setup
|
||||||
|
|
||||||
|
@ -120,6 +120,25 @@ ENABLE_HTTPS_SECURITY=false
|
|||||||
- `production` - Optimized for performance, minimal logging
|
- `production` - Optimized for performance, minimal logging
|
||||||
- **Example**: `production`
|
- **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`**
|
**`ENABLE_HTTPS_SECURITY`**
|
||||||
|
|
||||||
- **Purpose**: Enable/disable HTTPS security headers (Helmet middleware)
|
- **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 path from 'path';
|
||||||
import { config as dotenvConfig } from 'dotenv';
|
import { config as dotenvConfig } from 'dotenv';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
@ -10,12 +10,22 @@ const __dirname = dirname(__filename);
|
|||||||
// Load .env file from root directory
|
// Load .env file from root directory
|
||||||
dotenvConfig({ path: path.join(__dirname, '../../../.env') });
|
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 = {
|
export const config: Config = {
|
||||||
port: parseInt(process.env.PORT || '5172', 10),
|
port: parseInt(process.env.PORT || '5172', 10),
|
||||||
basePath: process.env.BASE_PATH || '/',
|
basePath: process.env.BASE_PATH || '/',
|
||||||
maxmindDbPath: process.env.MAXMIND_DB_PATH || '/usr/share/maxmind',
|
maxmindDbPath: process.env.MAXMIND_DB_PATH || '/usr/share/maxmind',
|
||||||
seqUrl: process.env.SEQ_URL,
|
seqUrl: process.env.SEQ_URL,
|
||||||
seqApiKey: process.env.SEQ_API_KEY,
|
seqApiKey: process.env.SEQ_API_KEY,
|
||||||
|
logLevel: parseLogLevel(process.env.LOG_LEVEL),
|
||||||
enableHttpsSecurity: process.env.ENABLE_HTTPS_SECURITY === 'true',
|
enableHttpsSecurity: process.env.ENABLE_HTTPS_SECURITY === 'true',
|
||||||
apiKeys: {
|
apiKeys: {
|
||||||
frontend: process.env.FRONTEND_API_KEY || 'frontend-default-key',
|
frontend: process.env.FRONTEND_API_KEY || 'frontend-default-key',
|
||||||
|
@ -96,7 +96,7 @@ class GeoIPService {
|
|||||||
const response: City = this.cityReader!.city(ip);
|
const response: City = this.cityReader!.city(ip);
|
||||||
const result: DetailedGeoIPResponse = {
|
const result: DetailedGeoIPResponse = {
|
||||||
ip,
|
ip,
|
||||||
location: response as any as GeoIPLocation,
|
location: response as GeoIPLocation,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.cache.set(cacheKey, result);
|
this.cache.set(cacheKey, result);
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
import { Logger as SeqLogger, SeqLoggerConfig } from 'seq-logging';
|
import { Logger as SeqLogger, SeqLoggerConfig } from 'seq-logging';
|
||||||
|
import { LogLevel, LOG_LEVELS, LOG_LEVEL_PRIORITY } from '../types/index.js';
|
||||||
import config from './config.js';
|
import config from './config.js';
|
||||||
|
|
||||||
class Logger {
|
class Logger {
|
||||||
private seqLogger?: SeqLogger;
|
private seqLogger?: SeqLogger;
|
||||||
|
private minLevel: LogLevel;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
this.minLevel = config.logLevel;
|
||||||
|
|
||||||
if (config.seqUrl) {
|
if (config.seqUrl) {
|
||||||
const seqConfig: SeqLoggerConfig = {
|
const seqConfig: SeqLoggerConfig = {
|
||||||
serverUrl: config.seqUrl,
|
serverUrl: config.seqUrl,
|
||||||
@ -15,9 +19,20 @@ class Logger {
|
|||||||
};
|
};
|
||||||
this.seqLogger = new SeqLogger(seqConfig);
|
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 || '');
|
console.log(`[INFO] ${message}`, properties || '');
|
||||||
if (this.seqLogger) {
|
if (this.seqLogger) {
|
||||||
this.seqLogger.emit({
|
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 || '');
|
console.warn(`[WARN] ${message}`, properties || '');
|
||||||
if (this.seqLogger) {
|
if (this.seqLogger) {
|
||||||
this.seqLogger.emit({
|
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 || '');
|
console.error(`[ERROR] ${message}`, error || '', properties || '');
|
||||||
if (this.seqLogger) {
|
if (this.seqLogger) {
|
||||||
this.seqLogger.emit({
|
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 || '');
|
console.debug(`[DEBUG] ${message}`, properties || '');
|
||||||
if (this.seqLogger) {
|
if (this.seqLogger) {
|
||||||
this.seqLogger.emit({
|
this.seqLogger.emit({
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
import { LogLevel } from './log';
|
||||||
|
|
||||||
|
export * from './log';
|
||||||
|
|
||||||
export interface GeoIPLocation {
|
export interface GeoIPLocation {
|
||||||
country?: {
|
country?: {
|
||||||
iso_code?: string;
|
iso_code?: string;
|
||||||
@ -69,6 +73,7 @@ export interface Config {
|
|||||||
maxmindDbPath: string;
|
maxmindDbPath: string;
|
||||||
seqUrl?: string;
|
seqUrl?: string;
|
||||||
seqApiKey?: string;
|
seqApiKey?: string;
|
||||||
|
logLevel: LogLevel;
|
||||||
enableHttpsSecurity: boolean;
|
enableHttpsSecurity: boolean;
|
||||||
apiKeys: {
|
apiKeys: {
|
||||||
frontend: string;
|
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