Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# A Docker Compose file for running the production build locally.
# Usage:
# docker compose up -d --build --force-recreate
services:
tag-page-rendering:
build:
dockerfile: ./Production.dockerfile
environment:
NODE_ENV: production
GU_STAGE: PROD
GU_APP: tag-page-rendering
GU_STACK: frontend

# We configure Log4JS based on this environment variable. See `server/lib/logging.ts`.
AWS_EXECUTION_ENV: AWS_ECS_LOCAL

# Explicitly tell AWS SDK where to find credentials
AWS_SHARED_CREDENTIALS_FILE: /.aws/credentials
ports:
- '9000:9000'

# Share the host's AWS credentials with the container
volumes:
- ${HOME}/.aws/credentials:/.aws/credentials:ro

# In Production.dockerfile, we're deliberately using a minimal image that does not have `curl` installed.
# Therefore, we're using Node to make a request to the healthcheck endpoint instead of using `curl`.
healthcheck:
test:
[
'CMD',
'node',
'-e',
"fetch('http://localhost:9000/_healthcheck').then(_ => process.exit(0)).catch(_ => process.exit(1))",
]
interval: 10s
timeout: 5s
retries: 5

# This service is used to make a sample request to tag-page-rendering only after it has started and is healthy.
# It exits immediately after making the request, so it is not a long-running service.
# View the logs via:
# docker logs "$(docker ps -aq --filter "name=sample-request" --latest)"
# The log output is the DCR response, so we can also pipe it through `jq` to pretty-print it, e.g.:
# docker logs "$(docker ps -aq --filter "name=sample-request" --latest)" | jq .
sample-request:
image: curlimages/curl:8.21.0
depends_on:
tag-page-rendering:
condition: service_healthy
command: |
curl "https://www.theguardian.com/tone/minutebyminute.json?dcr=true" --silent > data.json && \
curl -X POST http://localhost:9000/TagPage -d @data.json -H "Content-Type: application/json"
115 changes: 69 additions & 46 deletions dotcom-rendering/src/server/lib/logging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,6 @@ import type { Configuration, Layout, LoggingEvent } from 'log4js';
import { addLayout, configure, getLogger, shutdown } from 'log4js';
import { type DCRLoggingStore, loggingStore } from './logging-store';

const logName = `dotcom-rendering.log`;

const logLocation =
process.env.NODE_ENV === 'production' &&
!process.env.DISABLE_LOGGING_AND_METRICS
? `/var/log/dotcom-rendering/${logName}`
: `${path.resolve('logs')}/${logName}`;

type LogFields = Partial<DCRLoggingStore> &
Record<string | number | symbol, unknown>;

Expand Down Expand Up @@ -84,39 +76,69 @@ const disableLog4js: Configuration = {
},
};

const enableLog4js: Configuration = {
appenders: {
console: {
type: 'console',
layout: consoleLayout,
function configureLog4jsEC2() {
const logName = `dotcom-rendering.log`;

const logLocation =
process.env.NODE_ENV === 'production' &&
!process.env.DISABLE_LOGGING_AND_METRICS
? `/var/log/dotcom-rendering/${logName}`
: `${path.resolve('logs')}/${logName}`;

configure({
appenders: {
console: {
type: 'console',
layout: consoleLayout,
},
fileAppender: {
type: 'file',
filename: logLocation,
maxLogSize: '5M',
backups: 5,
compress: true,
layout: { type: 'json', separator: ',' },
// Owner Read & Write, Group Read
mode: 0o640,
},
out: {
type: 'stdout',
layout: { type: 'json', separator: ',' },
},
},
fileAppender: {
type: 'file',
filename: logLocation,
maxLogSize: '5M',
backups: 5,
compress: true,
layout: { type: 'json', separator: ',' },
// Owner Read & Write, Group Read
mode: 0o640,
categories: {
default: { appenders: ['out'], level: 'off' },
production: { appenders: ['out', 'fileAppender'], level: 'info' },
code: { appenders: ['out', 'fileAppender'], level: 'debug' },
development: { appenders: ['console'], level: 'debug' },
},
out: {
type: 'stdout',
layout: { type: 'json', separator: ',' },
// log4js cluster mode handling does not work as it prevents
// logs from processes other than the main process from
// writing to the log.
disableClustering: true,
});
}

function configureLog4jsECS() {
configure({
appenders: {
out: {
type: 'stdout',
layout: { type: 'json', separator: ',' },
},
},
},
categories: {
default: { appenders: ['out'], level: 'off' },
production: { appenders: ['out', 'fileAppender'], level: 'info' },
code: { appenders: ['out', 'fileAppender'], level: 'debug' },
development: { appenders: ['console'], level: 'debug' },
container: { appenders: ['out'], level: 'info' },
},
// log4js cluster mode handling does not work as it prevents
// logs from processes other than the main process from
// writing to the log.
disableClustering: true,
};
categories: {
default: { appenders: ['out'], level: 'info' },
production: { appenders: ['out'], level: 'info' },
code: { appenders: ['out'], level: 'debug' },
development: { appenders: ['out'], level: 'debug' },
},
// log4js cluster mode handling does not work as it prevents
// logs from processes other than the main process from
// writing to the log.
disableClustering: true,
});
}

// We do this to ensure no memory leaks during development as hot reloading
// doesn't clear up old listeners.
Expand All @@ -132,20 +154,21 @@ if (process.env.NODE_ENV === 'development') {
if (process.env.DISABLE_LOGGING_AND_METRICS === 'true') {
configure(disableLog4js);
} else {
configure(enableLog4js);
// See https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-environment-variables.html
const runningInECS =
process.env.AWS_EXECUTION_ENV?.startsWith('AWS_ECS_') === true;

if (runningInECS) {
configureLog4jsECS();
} else {
configureLog4jsEC2();
}
}

const getLoggerCategory = (): string => {
if (process.env.DISABLE_LOGGING_AND_METRICS === 'true') {
return 'off';
}

// Are we running in a container?
// See https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-environment-variables.html
if (process.env.AWS_EXECUTION_ENV?.startsWith('AWS_ECS_') === true) {
return 'container';
}

if (process.env.NODE_ENV === 'development') {
return 'development';
}
Expand Down
Loading