Skip to content

Commit e6b264c

Browse files
committed
feat(stream-recognizer): nest log context under metadata for New Relic
Match server NR log shape (metadata.*), stringify nested metadata for NR, dedupe stationID when metadata.stationID is present, and pass stationId into Shazam discovery logs. Read prior fingerprint from Redis metadata when skipping unchanged fingerprints.
1 parent 9b67bbe commit e6b264c

5 files changed

Lines changed: 250 additions & 127 deletions

File tree

‎stream-recognizer/src/index.js‎

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,11 @@ const server = listenHost
3535

3636
function onListen() {
3737
logger.info(
38-
listenHost
39-
? { port, host: listenHost, component: 'bootstrap' }
40-
: { port, component: 'bootstrap' },
38+
{
39+
metadata: listenHost
40+
? { port, host: listenHost, component: 'bootstrap' }
41+
: { port, component: 'bootstrap' },
42+
},
4143
'HTTP listening',
4244
);
4345
metrics.report('StreamRecognizerStarted', [
@@ -47,13 +49,13 @@ function onListen() {
4749
if (anyStationEnabled) {
4850
if (!probeBinary(ffmpegBin)) {
4951
logger.warn(
50-
{ bin: ffmpegBin, component: 'bootstrap' },
52+
{ metadata: { bin: ffmpegBin, component: 'bootstrap' } },
5153
`ffmpeg not found. ${missingBinaryHint('ffmpeg', 'FFMPEG_BIN')}`,
5254
);
5355
}
5456
if (!probeBinary(fpcalcBin)) {
5557
logger.warn(
56-
{ bin: fpcalcBin, component: 'bootstrap' },
58+
{ metadata: { bin: fpcalcBin, component: 'bootstrap' } },
5759
`fpcalc not found. ${missingBinaryHint('fpcalc', 'FPCALC_BIN')}`,
5860
);
5961
}
@@ -77,7 +79,10 @@ for (const station of stations) {
7779
recognitionBlacklist,
7880
}).catch((e) => {
7981
logger.error(
80-
{ err: e, stationID: station.id, component: 'bootstrap' },
82+
{
83+
err: e,
84+
metadata: { stationID: station.id, component: 'bootstrap' },
85+
},
8186
'Initial station tick failed',
8287
);
8388
});
@@ -86,7 +91,10 @@ for (const station of stations) {
8691
recognitionBlacklist,
8792
}).catch((e) => {
8893
logger.error(
89-
{ err: e, stationID: station.id, component: 'bootstrap' },
94+
{
95+
err: e,
96+
metadata: { stationID: station.id, component: 'bootstrap' },
97+
},
9098
'Scheduled station tick failed',
9199
);
92100
});
@@ -96,7 +104,7 @@ for (const station of stations) {
96104
}
97105

98106
function shutdown() {
99-
logger.info({ component: 'bootstrap' }, 'Shutting down');
107+
logger.info({ metadata: { component: 'bootstrap' } }, 'Shutting down');
100108
for (const t of timers) {
101109
clearInterval(t);
102110
}

‎stream-recognizer/src/lib/redis_store.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ export async function pingRedis(logger) {
168168
return { ok: true };
169169
} catch (e) {
170170
logger.error(
171-
{ err: e, component: 'redis', operation: 'ping' },
171+
{ err: e, metadata: { component: 'redis', operation: 'ping' } },
172172
'Redis ping failed',
173173
);
174174
return { ok: false, reason: String(e?.message || e) };

‎stream-recognizer/src/logger.js‎

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,36 @@ function isPlainObject(value) {
3737
return proto === Object.prototype || proto === null;
3838
}
3939

40+
const METADATA_NR_JSON_MAX = 8000;
41+
42+
/** Serialize nested metadata values for NR (primitives as-is; arrays/objects JSON-stringified). */
43+
function metadataValueForNr(mv) {
44+
if (mv === undefined) {
45+
return undefined;
46+
}
47+
if (
48+
mv === null ||
49+
typeof mv === 'string' ||
50+
typeof mv === 'number' ||
51+
typeof mv === 'boolean'
52+
) {
53+
return mv;
54+
}
55+
if (Array.isArray(mv) || isPlainObject(mv)) {
56+
try {
57+
const s = JSON.stringify(mv);
58+
return s.length > METADATA_NR_JSON_MAX ? `${s.slice(0, METADATA_NR_JSON_MAX)}…` : s;
59+
} catch {
60+
return String(mv);
61+
}
62+
}
63+
return String(mv);
64+
}
65+
4066
/**
41-
* Flat attributes for New Relic `recordLogEvent` (primitives only).
42-
* Nested `metadata` is flattened as `metadata.<key>` (same convention as server).
67+
* Flat attributes for New Relic `recordLogEvent`.
68+
* Nested `metadata` is flattened as `metadata.<key>` (same convention as server); non-primitive
69+
* metadata values are JSON-stringified for NR.
4370
*/
4471
function nrPrimitiveAttrs(o) {
4572
const out = {};
@@ -52,13 +79,9 @@ function nrPrimitiveAttrs(o) {
5279
}
5380
if (k === 'metadata' && isPlainObject(v)) {
5481
for (const [mk, mv] of Object.entries(v)) {
55-
if (
56-
mv === null ||
57-
typeof mv === 'string' ||
58-
typeof mv === 'number' ||
59-
typeof mv === 'boolean'
60-
) {
61-
out[`metadata.${mk}`] = mv;
82+
const serialized = metadataValueForNr(mv);
83+
if (serialized !== undefined) {
84+
out[`metadata.${mk}`] = serialized;
6285
}
6386
}
6487
continue;
@@ -124,6 +147,11 @@ function forwardLogToNewRelic(args, levelVal, log) {
124147
payload.error = errObj;
125148
}
126149

150+
// Align with server NR convention: prefer metadata.stationID over duplicate top-level binding.
151+
if (Object.prototype.hasOwnProperty.call(payload, 'metadata.stationID')) {
152+
delete payload.stationID;
153+
}
154+
127155
try {
128156
newrelic.recordLogEvent(payload);
129157
} catch {

0 commit comments

Comments
 (0)