fix(log-drain): send Coolify labels to Fluent Bit drains

Containers without the Coolify default variables sent no project,
environment, or service to the drain. The fluentd driver now also sends
the Coolify container labels; the built-in configs use them as a
fallback and add coolify.service_name.

Closes #8415

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Andras BacsaiandClaude Opus 5.5 committed 2026-11-03 22:42:37 +01:00
1 parent b83b97f7e6
commit 8d07d178f0
7 files changed
+99 -21

No files matched your search

+21 -19
View File
@@ -19,7 +19,7 @@ class StartLogDrain
* Bump when the generated files change (image, config, compose, or README), so that the
* server checks start running log drains again with the new files.
*/
public const CONFIG_VERSION = '1';
public const CONFIG_VERSION = '2';
public const CONFIG_VERSION_LABEL = 'coolify.logDrainConfigVersion';
@@ -56,6 +56,24 @@ class StartLogDrain
return 'Amazon CloudWatch Logs uses the built-in Docker awslogs driver.';
}
$serverName = $this->fluentBitValue((string) $server->name);
// Rename keeps a key that exists, so the container labels fill a field only when the
// Coolify default variables are not on the container. Then the labels are removed.
$coolifyFieldsFilter = "[FILTER]
Name modify
Match *
Set coolify.server_name {$serverName}
Rename COOLIFY_APP_NAME coolify.app_name
Rename COOLIFY_PROJECT_NAME coolify.project_name
Rename COOLIFY_SERVER_IP coolify.server_ip
Rename COOLIFY_ENVIRONMENT_NAME coolify.environment_name
Rename coolify.resourceName coolify.app_name
Rename coolify.projectName coolify.project_name
Rename coolify.environmentName coolify.environment_name
Rename coolify.serviceName coolify.service_name
Remove coolify.resourceName
Remove coolify.projectName
Remove coolify.environmentName
";
try {
if ($type === 'none') {
return 'No log drain is enabled.';
@@ -77,15 +95,7 @@ class StartLogDrain
Name grep
Match *
Exclude log 127.0.0.1
[FILTER]
Name modify
Match *
Set coolify.server_name {$serverName}
Rename COOLIFY_APP_NAME coolify.app_name
Rename COOLIFY_PROJECT_NAME coolify.project_name
Rename COOLIFY_SERVER_IP coolify.server_ip
Rename COOLIFY_ENVIRONMENT_NAME coolify.environment_name
[OUTPUT]
{$coolifyFieldsFilter}[OUTPUT]
Name nrlogs
Match *
license_key \${LICENSE_KEY}
@@ -111,15 +121,7 @@ class StartLogDrain
Name grep
Match *
Exclude log 127.0.0.1
[FILTER]
Name modify
Match *
Set coolify.server_name {$serverName}
Rename COOLIFY_APP_NAME coolify.app_name
Rename COOLIFY_PROJECT_NAME coolify.project_name
Rename COOLIFY_SERVER_IP coolify.server_ip
Rename COOLIFY_ENVIRONMENT_NAME coolify.environment_name
[OUTPUT]
{$coolifyFieldsFilter}[OUTPUT]
Name http
Match *
Host \${AXIOM_DOMAIN}
+3 -1
View File
@@ -4621,8 +4621,10 @@ function generate_fluentd_configuration(): array
'fluentd-address' => 'tcp://127.0.0.1:24224',
'fluentd-async' => 'true',
'fluentd-sub-second-precision' => 'true',
// env vars are used in the LogDrain configurations
// The env vars exist only when the resource includes the Coolify default variables.
'env' => 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME',
// Every Coolify container has these labels. Docker skips a label that a container does not have.
'labels' => 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName',
],
];
}
@@ -175,5 +175,5 @@ it('changes the configuration version when the generated files change', function
});
expect([StartLogDrain::CONFIG_VERSION, hash('sha256', $hashes->implode("\0"))])
->toBe(['1', '11916cd1142514c158a6fbe812ce468e3fe80fc79428cef6ba2654c86aa384ba']);
->toBe(['2', '4f4ceaf642f0de8009e256b54f2bbe2f842190297bbf2da92f2b31dd0a9baf3d']);
});
@@ -75,3 +75,30 @@ test('a server name with spaces is written as a single fluent-bit value', functi
'new relic' => [['is_logdrain_newrelic_enabled' => true, 'logdrain_newrelic_license_key' => 'key', 'logdrain_newrelic_base_uri' => 'https://log-api.newrelic.com/log/v1']],
'axiom' => [['is_logdrain_axiom_enabled' => true, 'logdrain_axiom_dataset_name' => 'dataset', 'logdrain_axiom_api_key' => 'key']],
]);
test('the built-in fluent-bit config uses the container labels only when the default variables are missing', function (array $settings) {
$team = Team::factory()->create();
$server = Server::factory()->create([
'team_id' => $team->id,
'private_key_id' => PrivateKey::factory()->create(['team_id' => $team->id])->id,
]);
$server->settings()->update($settings);
$commands = new Collection;
Process::fake(function ($process) use ($commands) {
$commands->push(remoteProcessScript($process));
return Process::result();
});
StartLogDrain::run($server->fresh());
preg_match("/echo '([A-Za-z0-9+\\/=]+)' \\| base64 -d \\| tee \\S+\\/fluent-bit\\.conf/", $commands->implode("\n"), $matches);
$config = base64_decode($matches[1]);
expect($config)->toMatch('/Rename\s+COOLIFY_PROJECT_NAME coolify\.project_name\n(.*\n)*\s+Rename\s+coolify\.projectName coolify\.project_name\n(.*\n)*\s+Remove\s+coolify\.projectName\n/')
->and($config)->toMatch('/Rename\s+coolify\.serviceName coolify\.service_name\n/');
})->with([
'new relic' => [['is_logdrain_newrelic_enabled' => true, 'logdrain_newrelic_license_key' => 'key', 'logdrain_newrelic_base_uri' => 'https://log-api.newrelic.com/log/v1']],
'axiom' => [['is_logdrain_axiom_enabled' => true, 'logdrain_axiom_dataset_name' => 'dataset', 'logdrain_axiom_api_key' => 'key']],
]);
@@ -180,6 +180,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
image: 'referenceapplication0001_web:abcdef0123456789abcdef0123456789abcdef01'
@@ -265,6 +266,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
my-app:
@@ -336,6 +338,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
api.v2:
@@ -390,6 +393,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
admin:
@@ -462,6 +466,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
blog:
@@ -537,6 +542,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
db:
@@ -602,6 +608,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
sidecar:
@@ -656,6 +663,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
worker:
@@ -718,6 +726,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
volumes:
@@ -949,6 +958,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
image: 'referenceapplication0001_web:abcdef0123456789abcdef0123456789abcdef01'
@@ -1048,6 +1058,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
my-app:
@@ -1128,6 +1139,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
api.v2:
@@ -1203,6 +1215,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
admin:
@@ -1284,6 +1297,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
blog:
@@ -1366,6 +1380,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
db:
@@ -1433,6 +1448,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
sidecar:
@@ -1489,6 +1505,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
worker:
@@ -1553,6 +1570,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
volumes:
@@ -1812,6 +1830,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
image: 'referenceapplication0001_web:pr-7'
@@ -1911,6 +1930,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
my-app-pr-7:
@@ -1996,6 +2016,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
api.v2-pr-7:
@@ -2076,6 +2097,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
admin-pr-7:
@@ -2162,6 +2184,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
blog-pr-7:
@@ -2248,6 +2271,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
db-pr-7:
@@ -2317,6 +2341,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
sidecar-pr-7:
@@ -2373,6 +2398,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
worker-pr-7:
@@ -2439,6 +2465,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
volumes:
@@ -2658,6 +2685,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
image: 'referenceapplication0001_web:pr-8'
@@ -2719,6 +2747,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
my-app-pr-8:
@@ -2766,6 +2795,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
api.v2-pr-8:
@@ -2812,6 +2842,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
admin-pr-8:
@@ -2860,6 +2891,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
blog-pr-8:
@@ -2907,6 +2939,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
db-pr-8:
@@ -2964,6 +2997,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
sidecar-pr-8:
@@ -3008,6 +3042,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
worker-pr-8:
@@ -3062,6 +3097,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
env_file:
- .env
volumes:
@@ -270,6 +270,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
api:
image: 'node:22-alpine'
build:
@@ -1149,6 +1150,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
api:
image: 'node:22-alpine'
build:
@@ -2071,6 +2073,7 @@
fluentd-async: 'true'
fluentd-sub-second-precision: 'true'
env: 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME'
labels: 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName'
api:
image: 'node:22-alpine'
build:
@@ -75,3 +75,11 @@ it('reports CloudWatch as an enabled drain that does not use Fluent Bit', functi
->and($axiom->isLogDrainEnabled())->toBeTrue()
->and($axiom->isFluentBitLogDrainEnabled())->toBeTrue();
});
it('sends the Coolify labels with fluentd, so containers without the default variables are identified', function () {
$server = serverWithLogDrainSettings(['is_logdrain_custom_enabled' => true]);
expect(generate_log_drain_configuration($server)['options'])
->toHaveKey('env', 'COOLIFY_APP_NAME,COOLIFY_PROJECT_NAME,COOLIFY_SERVER_IP,COOLIFY_ENVIRONMENT_NAME')
->toHaveKey('labels', 'coolify.projectName,coolify.environmentName,coolify.resourceName,coolify.serviceName');
});