Skip to content

Commit 9b7a3cc

Browse files
committed
Setup axiom logging configuration
1 parent 9a3eb43 commit 9b7a3cc

5 files changed

Lines changed: 168 additions & 5 deletions

File tree

.env.example

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,15 @@ TINYBIRD_TRACKER_TOKEN=p.eyJxxxxxxxx
99
# Google Cloud Project Configuration (required when using firestore salt store)
1010
GOOGLE_CLOUD_PROJECT=traffic-analytics-dev
1111

12+
# Optional Axiom secondary log transport for production logging
13+
# AXIOM_DATASET=traffic-analytics
14+
# AXIOM_TOKEN=xaat-xxxxxxxx
15+
# AXIOM_ORG_ID=
16+
# AXIOM_URL=https://api.axiom.co
17+
# AXIOM_LOG_LEVEL=info
18+
1219
# Firestore Configuration (required when using firestore salt store)
1320
FIRESTORE_DATABASE_ID=
1421

1522
# Pub/Sub Configuration (required for pub/sub functionality)
16-
PUBSUB_TOPIC_PAGE_HITS_RAW=
23+
PUBSUB_TOPIC_PAGE_HITS_RAW=

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
"vitest": "4.1.5"
5858
},
5959
"dependencies": {
60+
"@axiomhq/pino": "^1.5.0",
6061
"@fastify/cors": "11.2.0",
6162
"@fastify/reply-from": "12.6.2",
6263
"@fastify/type-provider-typebox": "5.2.0",

src/utils/logger-config.ts

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type {LoggerOptions} from 'pino';
1+
import type {LoggerOptions, TransportTargetOptions} from 'pino';
22
import type {PrettyOptions} from 'pino-pretty';
33
import type {FastifyRequest, FastifyReply} from 'fastify';
44
import {createGcpLoggingPinoConfig} from '@google-cloud/pino-logging-gcp-config';
@@ -11,6 +11,50 @@ function getServiceContext(): {service: string; version?: string} {
1111
return version ? {service, version} : {service};
1212
}
1313

14+
function getStdoutTransportTarget(): TransportTargetOptions {
15+
return {
16+
target: 'pino/file',
17+
options: {
18+
destination: 1
19+
}
20+
};
21+
}
22+
23+
function getAxiomTransportTarget(): TransportTargetOptions | undefined {
24+
const token = process.env.AXIOM_TOKEN;
25+
const dataset = process.env.AXIOM_DATASET;
26+
27+
if (!token || !dataset) {
28+
return undefined;
29+
}
30+
31+
return {
32+
target: '@axiomhq/pino',
33+
level: process.env.AXIOM_LOG_LEVEL || process.env.LOG_LEVEL || 'info',
34+
options: {
35+
token,
36+
dataset,
37+
...(process.env.AXIOM_ORG_ID ? {orgId: process.env.AXIOM_ORG_ID} : {}),
38+
...(process.env.AXIOM_URL ? {url: process.env.AXIOM_URL} : {})
39+
}
40+
};
41+
}
42+
43+
function getProductionTransport(): LoggerOptions['transport'] | undefined {
44+
const axiomTarget = getAxiomTransportTarget();
45+
46+
if (!axiomTarget) {
47+
return undefined;
48+
}
49+
50+
return {
51+
targets: [
52+
getStdoutTransportTarget(),
53+
axiomTarget
54+
]
55+
};
56+
}
57+
1458
/**
1559
* Get logger configuration based on environment
1660
*/
@@ -51,7 +95,7 @@ export function getLoggerConfig(): LoggerOptions {
5195
}
5296

5397
// Production / staging configuration - GCP optimized JSON logs
54-
return createGcpLoggingPinoConfig(
98+
const config = createGcpLoggingPinoConfig(
5599
{
56100
serviceContext: getServiceContext(),
57101
inihibitDiagnosticMessage: Boolean(process.env.VITEST)
@@ -60,4 +104,8 @@ export function getLoggerConfig(): LoggerOptions {
60104
level: process.env.LOG_LEVEL || 'info'
61105
}
62106
);
107+
108+
const transport = getProductionTransport();
109+
110+
return transport ? {...config, transport} : config;
63111
}

test/unit/utils/logger.test.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,51 @@ describe('Logger Config', () => {
8787
expect(config.formatters).toHaveProperty('log');
8888
expect(config.serializers).toBeUndefined();
8989
});
90+
91+
it('should add an Axiom transport in production when dataset and token are configured', () => {
92+
vi.stubEnv('NODE_ENV', 'production');
93+
vi.stubEnv('LOG_LEVEL', 'info');
94+
vi.stubEnv('AXIOM_DATASET', 'traffic-logs');
95+
vi.stubEnv('AXIOM_TOKEN', 'axiom-token');
96+
vi.stubEnv('AXIOM_ORG_ID', 'ghost');
97+
vi.stubEnv('AXIOM_URL', 'https://api.axiom.test');
98+
vi.stubEnv('AXIOM_LOG_LEVEL', 'error');
99+
100+
const config = getLoggerConfig();
101+
102+
expect(config.transport).toMatchObject({
103+
targets: [
104+
{
105+
target: 'pino/file',
106+
options: {
107+
destination: 1
108+
}
109+
},
110+
{
111+
target: '@axiomhq/pino',
112+
level: 'error',
113+
options: {
114+
dataset: 'traffic-logs',
115+
token: 'axiom-token',
116+
orgId: 'ghost',
117+
url: 'https://api.axiom.test'
118+
}
119+
}
120+
]
121+
});
122+
expect(config.formatters).toHaveProperty('log');
123+
});
124+
125+
it('should skip the Axiom transport when dataset or token are missing', () => {
126+
vi.stubEnv('NODE_ENV', 'production');
127+
vi.stubEnv('LOG_LEVEL', 'info');
128+
vi.stubEnv('AXIOM_DATASET', 'traffic-logs');
129+
130+
const config = getLoggerConfig();
131+
132+
expect(config.transport).toBeUndefined();
133+
expect(config.formatters).toHaveProperty('log');
134+
});
90135
});
91136

92137
describe('production logging output', () => {

yarn.lock

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,21 @@
22
# yarn lockfile v1
33

44

5+
"@axiomhq/js@1.5.0":
6+
version "1.5.0"
7+
resolved "https://registry.yarnpkg.com/@axiomhq/js/-/js-1.5.0.tgz#2501d488fe1baa69f87172d88cef506cc904cf92"
8+
integrity sha512-7SkfgnrKpjRs86h4TIcoZQ1TltKG0+WcQtMkBa5N3iSuGT5+aIpnbLFrRfbfhwhxzysQkF01qNW4fNUtePqjDg==
9+
dependencies:
10+
fetch-retry "^6.0.0"
11+
12+
"@axiomhq/pino@^1.5.0":
13+
version "1.5.0"
14+
resolved "https://registry.yarnpkg.com/@axiomhq/pino/-/pino-1.5.0.tgz#c97896f72551189810d53190e9a8976ffd2e6704"
15+
integrity sha512-DaKaauddf+HuEqdwUrvU9RzXavCwjY/ZrVS4lHZpSbRw4i7hET5y8moE3Uo93em/S7ZaUFSOSYsRujZxGecq0A==
16+
dependencies:
17+
"@axiomhq/js" "1.5.0"
18+
pino-abstract-transport "^1.2.0"
19+
520
"@babel/code-frame@^7.0.0":
621
version "7.29.0"
722
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.29.0.tgz#7cd7a59f15b3cc0dcd803038f7792712a7d0b15c"
@@ -2095,7 +2110,7 @@ balanced-match@^4.0.2:
20952110
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-4.0.4.tgz#bfb10662feed8196a2c62e7c68e17720c274179a"
20962111
integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==
20972112

2098-
base64-js@^1.3.0:
2113+
base64-js@^1.3.0, base64-js@^1.3.1:
20992114
version "1.5.1"
21002115
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
21012116
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
@@ -2132,6 +2147,14 @@ buffer-equal-constant-time@^1.0.1:
21322147
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
21332148
integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==
21342149

2150+
buffer@^6.0.3:
2151+
version "6.0.3"
2152+
resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6"
2153+
integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
2154+
dependencies:
2155+
base64-js "^1.3.1"
2156+
ieee754 "^1.2.1"
2157+
21352158
builtin-modules@^3.3.0:
21362159
version "3.3.0"
21372160
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6"
@@ -2763,6 +2786,11 @@ eventid@^2.0.0, eventid@^2.0.1:
27632786
dependencies:
27642787
uuid "^8.0.0"
27652788

2789+
events@^3.3.0:
2790+
version "3.3.0"
2791+
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
2792+
integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
2793+
27662794
expect-type@^1.3.0:
27672795
version "1.3.0"
27682796
resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.3.0.tgz#0d58ed361877a31bbc4dd6cf71bbfef7faf6bd68"
@@ -2878,6 +2906,11 @@ fetch-blob@^3.1.2, fetch-blob@^3.1.4:
28782906
node-domexception "^1.0.0"
28792907
web-streams-polyfill "^3.0.3"
28802908

2909+
fetch-retry@^6.0.0:
2910+
version "6.0.0"
2911+
resolved "https://registry.yarnpkg.com/fetch-retry/-/fetch-retry-6.0.0.tgz#4ffdf92c834d72ae819e42a4ee2a63f1e9454426"
2912+
integrity sha512-BUFj1aMubgib37I3v4q78fYo63Po7t4HUPTpQ6/QE6yK6cIQrP+W43FYToeTEyg5m2Y7eFUtijUuAv/PDlWuag==
2913+
28812914
file-entry-cache@^6.0.1:
28822915
version "6.0.1"
28832916
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
@@ -3307,6 +3340,11 @@ husky@9.1.7:
33073340
resolved "https://registry.yarnpkg.com/husky/-/husky-9.1.7.tgz#d46a38035d101b46a70456a850ff4201344c0b2d"
33083341
integrity sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==
33093342

3343+
ieee754@^1.2.1:
3344+
version "1.2.1"
3345+
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
3346+
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
3347+
33103348
ignore@^5.2.0, ignore@^5.3.2:
33113349
version "5.3.2"
33123350
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
@@ -4034,6 +4072,14 @@ picomatch@^4.0.2, picomatch@^4.0.3, picomatch@^4.0.4:
40344072
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.4.tgz#fd6f5e00a143086e074dffe4c924b8fb293b0589"
40354073
integrity sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==
40364074

4075+
pino-abstract-transport@^1.2.0:
4076+
version "1.2.0"
4077+
resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz#97f9f2631931e242da531b5c66d3079c12c9d1b5"
4078+
integrity sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==
4079+
dependencies:
4080+
readable-stream "^4.0.0"
4081+
split2 "^4.0.0"
4082+
40374083
pino-abstract-transport@^3.0.0:
40384084
version "3.0.0"
40394085
resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-3.0.0.tgz#b21e5f33a297e8c4c915c62b3ce5dd4a87a52c23"
@@ -4147,6 +4193,11 @@ process-warning@^5.0.0:
41474193
resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-5.0.0.tgz#566e0bf79d1dff30a72d8bbbe9e8ecefe8d378d7"
41484194
integrity sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==
41494195

4196+
process@^0.11.10:
4197+
version "0.11.10"
4198+
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
4199+
integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==
4200+
41504201
proto3-json-serializer@^2.0.2:
41514202
version "2.0.2"
41524203
resolved "https://registry.yarnpkg.com/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz#5b705203b4d58f3880596c95fad64902617529dd"
@@ -4269,6 +4320,17 @@ readable-stream@^3.1.1:
42694320
string_decoder "^1.1.1"
42704321
util-deprecate "^1.0.1"
42714322

4323+
readable-stream@^4.0.0:
4324+
version "4.7.0"
4325+
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.7.0.tgz#cedbd8a1146c13dfff8dab14068028d58c15ac91"
4326+
integrity sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==
4327+
dependencies:
4328+
abort-controller "^3.0.0"
4329+
buffer "^6.0.3"
4330+
events "^3.3.0"
4331+
process "^0.11.10"
4332+
string_decoder "^1.3.0"
4333+
42724334
real-require@^0.2.0:
42734335
version "0.2.0"
42744336
resolved "https://registry.yarnpkg.com/real-require/-/real-require-0.2.0.tgz#209632dea1810be2ae063a6ac084fee7e33fba78"
@@ -4615,7 +4677,7 @@ string-width@^5.0.1, string-width@^5.1.2:
46154677
emoji-regex "^9.2.2"
46164678
strip-ansi "^7.0.1"
46174679

4618-
string_decoder@^1.1.1:
4680+
string_decoder@^1.1.1, string_decoder@^1.3.0:
46194681
version "1.3.0"
46204682
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
46214683
integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==

0 commit comments

Comments
 (0)