Skip to content

Commit c83eb28

Browse files
committed
Setup axiom logging configuration
1 parent a6d8e18 commit c83eb28

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.2"
5858
},
5959
"dependencies": {
60+
"@axiomhq/pino": "^1.5.0",
6061
"@fastify/cors": "11.2.0",
6162
"@fastify/reply-from": "12.6.1",
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"
@@ -2066,7 +2081,7 @@ balanced-match@^4.0.2:
20662081
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-4.0.4.tgz#bfb10662feed8196a2c62e7c68e17720c274179a"
20672082
integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==
20682083

2069-
base64-js@^1.3.0:
2084+
base64-js@^1.3.0, base64-js@^1.3.1:
20702085
version "1.5.1"
20712086
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
20722087
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
@@ -2103,6 +2118,14 @@ buffer-equal-constant-time@^1.0.1:
21032118
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
21042119
integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==
21052120

2121+
buffer@^6.0.3:
2122+
version "6.0.3"
2123+
resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6"
2124+
integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
2125+
dependencies:
2126+
base64-js "^1.3.1"
2127+
ieee754 "^1.2.1"
2128+
21062129
builtin-modules@^3.3.0:
21072130
version "3.3.0"
21082131
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6"
@@ -2734,6 +2757,11 @@ eventid@^2.0.0, eventid@^2.0.1:
27342757
dependencies:
27352758
uuid "^8.0.0"
27362759

2760+
events@^3.3.0:
2761+
version "3.3.0"
2762+
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
2763+
integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
2764+
27372765
expect-type@^1.3.0:
27382766
version "1.3.0"
27392767
resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.3.0.tgz#0d58ed361877a31bbc4dd6cf71bbfef7faf6bd68"
@@ -2849,6 +2877,11 @@ fetch-blob@^3.1.2, fetch-blob@^3.1.4:
28492877
node-domexception "^1.0.0"
28502878
web-streams-polyfill "^3.0.3"
28512879

2880+
fetch-retry@^6.0.0:
2881+
version "6.0.0"
2882+
resolved "https://registry.yarnpkg.com/fetch-retry/-/fetch-retry-6.0.0.tgz#4ffdf92c834d72ae819e42a4ee2a63f1e9454426"
2883+
integrity sha512-BUFj1aMubgib37I3v4q78fYo63Po7t4HUPTpQ6/QE6yK6cIQrP+W43FYToeTEyg5m2Y7eFUtijUuAv/PDlWuag==
2884+
28522885
file-entry-cache@^6.0.1:
28532886
version "6.0.1"
28542887
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
@@ -3278,6 +3311,11 @@ husky@9.1.7:
32783311
resolved "https://registry.yarnpkg.com/husky/-/husky-9.1.7.tgz#d46a38035d101b46a70456a850ff4201344c0b2d"
32793312
integrity sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==
32803313

3314+
ieee754@^1.2.1:
3315+
version "1.2.1"
3316+
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
3317+
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
3318+
32813319
ignore@^5.2.0, ignore@^5.3.2:
32823320
version "5.3.2"
32833321
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
@@ -4005,6 +4043,14 @@ picomatch@^4.0.2, picomatch@^4.0.3, picomatch@^4.0.4:
40054043
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.4.tgz#fd6f5e00a143086e074dffe4c924b8fb293b0589"
40064044
integrity sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==
40074045

4046+
pino-abstract-transport@^1.2.0:
4047+
version "1.2.0"
4048+
resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz#97f9f2631931e242da531b5c66d3079c12c9d1b5"
4049+
integrity sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==
4050+
dependencies:
4051+
readable-stream "^4.0.0"
4052+
split2 "^4.0.0"
4053+
40084054
pino-abstract-transport@^3.0.0:
40094055
version "3.0.0"
40104056
resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-3.0.0.tgz#b21e5f33a297e8c4c915c62b3ce5dd4a87a52c23"
@@ -4118,6 +4164,11 @@ process-warning@^5.0.0:
41184164
resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-5.0.0.tgz#566e0bf79d1dff30a72d8bbbe9e8ecefe8d378d7"
41194165
integrity sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==
41204166

4167+
process@^0.11.10:
4168+
version "0.11.10"
4169+
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
4170+
integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==
4171+
41214172
proto3-json-serializer@^2.0.2:
41224173
version "2.0.2"
41234174
resolved "https://registry.yarnpkg.com/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz#5b705203b4d58f3880596c95fad64902617529dd"
@@ -4222,6 +4273,17 @@ readable-stream@^3.1.1:
42224273
string_decoder "^1.1.1"
42234274
util-deprecate "^1.0.1"
42244275

4276+
readable-stream@^4.0.0:
4277+
version "4.7.0"
4278+
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.7.0.tgz#cedbd8a1146c13dfff8dab14068028d58c15ac91"
4279+
integrity sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==
4280+
dependencies:
4281+
abort-controller "^3.0.0"
4282+
buffer "^6.0.3"
4283+
events "^3.3.0"
4284+
process "^0.11.10"
4285+
string_decoder "^1.3.0"
4286+
42254287
real-require@^0.2.0:
42264288
version "0.2.0"
42274289
resolved "https://registry.yarnpkg.com/real-require/-/real-require-0.2.0.tgz#209632dea1810be2ae063a6ac084fee7e33fba78"
@@ -4567,7 +4629,7 @@ string-width@^5.0.1, string-width@^5.1.2:
45674629
emoji-regex "^9.2.2"
45684630
strip-ansi "^7.0.1"
45694631

4570-
string_decoder@^1.1.1:
4632+
string_decoder@^1.1.1, string_decoder@^1.3.0:
45714633
version "1.3.0"
45724634
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
45734635
integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==

0 commit comments

Comments
 (0)