feat(logging): add ipAddress to default log metadata
Add getLocalIpAddress() that reliably resolves the primary LAN IPv4 address by collecting all non-loopback, non-APIPA addresses and prioritizing RFC 1918 private ranges (192.168.x.x, 10.x.x.x, 172.16-31.x.x) over public IPs. Falls back to any non-internal address or 'N/A'. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -34,6 +34,52 @@ const PROJECT_LEVELS = {
|
|||||||
// Cache isProduction() at module load — app.isPackaged never changes at runtime
|
// Cache isProduction() at module load — app.isPackaged never changes at runtime
|
||||||
const IS_PROD = isProduction()
|
const IS_PROD = isProduction()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if an IPv4 address is an RFC 1918 private address.
|
||||||
|
* Private ranges: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
|
||||||
|
*/
|
||||||
|
function isPrivateIpv4(ip: string): boolean {
|
||||||
|
const parts = ip.split('.').map(Number)
|
||||||
|
if (parts.length !== 4) return false
|
||||||
|
// 10.0.0.0/8
|
||||||
|
if (parts[0] === 10) return true
|
||||||
|
// 172.16.0.0/12
|
||||||
|
if (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31) return true
|
||||||
|
// 192.168.0.0/16
|
||||||
|
if (parts[0] === 192 && parts[1] === 168) return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the primary local IPv4 address (RFC 1918 private address).
|
||||||
|
*
|
||||||
|
* Strategy:
|
||||||
|
* 1. Collect all non-internal, non-APIPA IPv4 addresses from physical adapters
|
||||||
|
* 2. Return the first private (LAN) address found
|
||||||
|
* 3. Fallback to any remaining non-internal address
|
||||||
|
* 4. Returns 'N/A' if none found
|
||||||
|
*/
|
||||||
|
function getLocalIpAddress(): string {
|
||||||
|
const interfaces = os.networkInterfaces()
|
||||||
|
const candidates: string[] = []
|
||||||
|
|
||||||
|
for (const [, addrs] of Object.entries(interfaces)) {
|
||||||
|
if (!addrs) continue
|
||||||
|
for (const iface of addrs) {
|
||||||
|
if (iface.family === 'IPv4' && !iface.internal && !iface.address.startsWith('169.254.')) {
|
||||||
|
candidates.push(iface.address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefer private (LAN) addresses
|
||||||
|
const privateIp = candidates.find(isPrivateIpv4)
|
||||||
|
if (privateIp) return privateIp
|
||||||
|
|
||||||
|
// Fallback: any non-internal address
|
||||||
|
return candidates[0] || 'N/A'
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if an error has already been serialized (plain object with name/message but not an Error instance).
|
* Check if an error has already been serialized (plain object with name/message but not an Error instance).
|
||||||
* Prevents double-serialization when logError() output passes through the format pipeline.
|
* Prevents double-serialization when logError() output passes through the format pipeline.
|
||||||
@@ -172,7 +218,8 @@ const logger = winston.createLogger({
|
|||||||
defaultMeta: {
|
defaultMeta: {
|
||||||
service: 'erpauto',
|
service: 'erpauto',
|
||||||
appVersion: app.getVersion(),
|
appVersion: app.getVersion(),
|
||||||
computerName: os.hostname()
|
computerName: os.hostname(),
|
||||||
|
ipAddress: getLocalIpAddress()
|
||||||
},
|
},
|
||||||
transports: [
|
transports: [
|
||||||
// Console transport - always enabled
|
// Console transport - always enabled
|
||||||
|
|||||||
Reference in New Issue
Block a user