diff --git a/README.md b/README.md index 3d3e1abf5..8c8edf60a 100644 --- a/README.md +++ b/README.md @@ -10,35 +10,40 @@ # About the Project For more information, take a look at our landing page [here](https://coolify.io). -> If you are looking for previous (v3) version, it is [here](https://github.com/coollabsio/coolify/tree/v3). +# Donations +To stay completely free, open-source, no feature behind paywall and evolve the project, we need your help. If you like Coolify, please consider donating to help us fund the future development of the project. + +https://coolify.io/sponsorships + +Thank you so much! # Cloud If you do not want to self-host Coolify, there is a paid cloud version available: https://app.coolify.io -You can easily attach your own servers, get all the automations, free email notifications, etc. - For more information & pricing, take a look at our landing page [here](https://coolify.io). -# Beta +## Why should I use the Cloud version? +The recommended way to use Coolify is to have one server for Coolify and one (or more) for the resources you are deploying. A server is around 4-5$/month. -The latest version (v4) is still in beta. That does not mean it is unstable. All the features that are available are stable enough be usable in real-life. - -There are hundreds of people using it for managing their client's applications, freelancers, hobbyists, businesses. +By subscribing to the cloud version, you get the Coolify server for the same price, but with: +- High-availability +- Free email notifications +- Better support +- Less maintenance for you # Installation ```bash curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash ``` +You can find the installation script source [here](./scripts/install.sh). -You can find the installation script [here](./scripts/install.sh). - -## Support +# Support Contact us [here](https://coolify.io/docs/contact). -## Recognitions +# Recognitions

@@ -54,11 +59,11 @@ ## Recognitions coollabsio%2Fcoolify | Trendshift -## 💰 Financial Contributors +# 💰 Financial Contributors Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/coollabsio/contribute)] -### Organizations +## Organizations Special thanks to our biggest sponsors, [CCCareers](https://cccareers.org/) and [Appwrite](https://appwrite.io)! @@ -78,10 +83,10 @@ ### Organizations -### Individuals +## Individuals -## Star History +# Star History [![Star History Chart](https://api.star-history.com/svg?repos=coollabsio/coolify&type=Date)](https://star-history.com/#coollabsio/coolify&Date) diff --git a/app/Actions/Database/StartMariadb.php b/app/Actions/Database/StartMariadb.php index d42b53c09..3e32c2481 100644 --- a/app/Actions/Database/StartMariadb.php +++ b/app/Actions/Database/StartMariadb.php @@ -69,6 +69,16 @@ public function handle(StandaloneMariadb $database) ] ] ]; + if ($this->database->destination->server->isDrainLogActivated()) { + $docker_compose['services'][$container_name]['logging'] = [ + 'driver' => 'fluentd', + 'options' => [ + 'fluentd-address' => "tcp://127.0.0.1:24224", + 'fluentd-async' => "true", + 'fluentd-sub-second-precision' => "true", + ] + ]; + } if (count($this->database->ports_mappings_array) > 0) { $docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array; } diff --git a/app/Actions/Database/StartMongodb.php b/app/Actions/Database/StartMongodb.php index c1f1b71df..4ebbb9ec4 100644 --- a/app/Actions/Database/StartMongodb.php +++ b/app/Actions/Database/StartMongodb.php @@ -76,6 +76,16 @@ public function handle(StandaloneMongodb $database) ] ] ]; + if ($this->database->destination->server->isDrainLogActivated()) { + $docker_compose['services'][$container_name]['logging'] = [ + 'driver' => 'fluentd', + 'options' => [ + 'fluentd-address' => "tcp://127.0.0.1:24224", + 'fluentd-async' => "true", + 'fluentd-sub-second-precision' => "true", + ] + ]; + } if (count($this->database->ports_mappings_array) > 0) { $docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array; } diff --git a/app/Actions/Database/StartMysql.php b/app/Actions/Database/StartMysql.php index 4cf4282b6..c3f04dc9f 100644 --- a/app/Actions/Database/StartMysql.php +++ b/app/Actions/Database/StartMysql.php @@ -69,6 +69,16 @@ public function handle(StandaloneMysql $database) ] ] ]; + if ($this->database->destination->server->isDrainLogActivated()) { + $docker_compose['services'][$container_name]['logging'] = [ + 'driver' => 'fluentd', + 'options' => [ + 'fluentd-address' => "tcp://127.0.0.1:24224", + 'fluentd-async' => "true", + 'fluentd-sub-second-precision' => "true", + ] + ]; + } if (count($this->database->ports_mappings_array) > 0) { $docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array; } diff --git a/app/Actions/Database/StartPostgresql.php b/app/Actions/Database/StartPostgresql.php index 6eefd45ff..1acdcf957 100644 --- a/app/Actions/Database/StartPostgresql.php +++ b/app/Actions/Database/StartPostgresql.php @@ -79,6 +79,16 @@ public function handle(StandalonePostgresql $database) ] ] ]; + if ($this->database->destination->server->isDrainLogActivated()) { + $docker_compose['services'][$container_name]['logging'] = [ + 'driver' => 'fluentd', + 'options' => [ + 'fluentd-address' => "tcp://127.0.0.1:24224", + 'fluentd-async' => "true", + 'fluentd-sub-second-precision' => "true", + ] + ]; + } if (count($this->database->ports_mappings_array) > 0) { $docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array; } diff --git a/app/Actions/Database/StartRedis.php b/app/Actions/Database/StartRedis.php index a585678b3..0cbd01f63 100644 --- a/app/Actions/Database/StartRedis.php +++ b/app/Actions/Database/StartRedis.php @@ -78,6 +78,16 @@ public function handle(StandaloneRedis $database) ] ] ]; + if ($this->database->destination->server->isDrainLogActivated()) { + $docker_compose['services'][$container_name]['logging'] = [ + 'driver' => 'fluentd', + 'options' => [ + 'fluentd-address' => "tcp://127.0.0.1:24224", + 'fluentd-async' => "true", + 'fluentd-sub-second-precision' => "true", + ] + ]; + } if (count($this->database->ports_mappings_array) > 0) { $docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array; } diff --git a/app/Actions/Server/InstallLogDrain.php b/app/Actions/Server/InstallLogDrain.php new file mode 100644 index 000000000..5ea1d5e43 --- /dev/null +++ b/app/Actions/Server/InstallLogDrain.php @@ -0,0 +1,185 @@ +settings->is_logdrain_newrelic_enabled) { + throw new \Exception('New Relic log drain is not enabled.'); + } + $config = base64_encode(" +[SERVICE] + Flush 5 + Daemon off + Tag container_logs + Log_Level debug + Parsers_File parsers.conf +[INPUT] + Name forward + Buffer_Chunk_Size 1M + Buffer_Max_Size 6M +[FILTER] + Name grep + Match * + Exclude log 127.0.0.1 +[FILTER] + Name modify + Match * + Set server_name {$server->name} +[OUTPUT] + Name nrlogs + Match * + license_key \${LICENSE_KEY} + # https://log-api.eu.newrelic.com/log/v1 - EU + # https://log-api.newrelic.com/log/v1 - US + base_uri \${BASE_URI} +"); + } else if ($type === 'highlight') { + if (!$server->settings->is_logdrain_highlight_enabled) { + throw new \Exception('Highlight log drain is not enabled.'); + } + $config = base64_encode(" +[SERVICE] + Flush 5 + Daemon off + Log_Level debug + Parsers_File parsers.conf +[INPUT] + Name forward + tag \${HIGHLIGHT_PROJECT_ID} + Buffer_Chunk_Size 1M + Buffer_Max_Size 6M +[OUTPUT] + Name forward + Match * + Host otel.highlight.io + Port 24224 +"); + } else if ($type === 'axiom') { + if (!$server->settings->is_logdrain_axiom_enabled) { + throw new \Exception('Axiom log drain is not enabled.'); + } + $config = base64_encode(" +[SERVICE] + Flush 5 + Daemon off + Log_Level debug + Parsers_File parsers.conf +[INPUT] + Name forward + Buffer_Chunk_Size 1M + Buffer_Max_Size 6M +[FILTER] + Name grep + Match * + Exclude log 127.0.0.1 +[FILTER] + Name modify + Match * + Set server_name {$server->name} +[OUTPUT] + Name http + Match * + Host api.axiom.co + Port 443 + URI /v1/datasets/\${AXIOM_DATASET_NAME}/ingest + # Authorization Bearer should be an API token + Header Authorization Bearer \${AXIOM_API_KEY} + compress gzip + format json + json_date_key _time + json_date_format iso8601 + tls On +"); + } else { + throw new \Exception('Unknown log drain type.'); + } + $parsers = base64_encode(" +[PARSER] + Name empty_line_skipper + Format regex + Regex /^(?!\s*$).+/ +"); + $compose = base64_encode(" +services: + coolify-log-drain: + image: cr.fluentbit.io/fluent/fluent-bit:2.0 + container_name: coolify-log-drain + command: -c /fluent-bit.conf + env_file: + - .env + volumes: + - ./fluent-bit.conf:/fluent-bit.conf + - ./parsers.conf:/parsers.conf + ports: + - 127.0.0.1:24224:24224 +"); + $readme = base64_encode('# New Relic Log Drain +This log drain is based on [Fluent Bit](https://fluentbit.io/) and New Relic Log Forwarder. + +Files: +- `fluent-bit.conf` - configuration file for Fluent Bit +- `docker-compose.yml` - docker-compose file to run Fluent Bit +- `.env` - environment variables for Fluent Bit +'); + $license_key = $server->settings->logdrain_newrelic_license_key; + $base_uri = $server->settings->logdrain_newrelic_base_uri; + $base_path = config('coolify.base_config_path'); + + $config_path = $base_path . '/log-drains'; + $fluent_bit_config = $config_path . '/fluent-bit.conf'; + $parsers_config = $config_path . '/parsers.conf'; + $compose_path = $config_path . '/docker-compose.yml'; + $readme_path = $config_path . '/README.md'; + $command = [ + "echo 'Saving configuration'", + "mkdir -p $config_path", + "echo '{$parsers}' | base64 -d > $parsers_config", + "echo '{$config}' | base64 -d > $fluent_bit_config", + "echo '{$compose}' | base64 -d > $compose_path", + "echo '{$readme}' | base64 -d > $readme_path", + "test -f $config_path/.env && rm $config_path/.env", + + ]; + if ($type === 'newrelic') { + $add_envs_command = [ + "echo LICENSE_KEY=$license_key >> $config_path/.env", + "echo BASE_URI=$base_uri >> $config_path/.env", + ]; + } else if ($type === 'highlight') { + $add_envs_command = [ + "echo HIGHLIGHT_PROJECT_ID={$server->settings->logdrain_highlight_project_id} >> $config_path/.env", + ]; + } else if ($type === 'axiom') { + $add_envs_command = [ + "echo AXIOM_DATASET_NAME={$server->settings->logdrain_axiom_dataset_name} >> $config_path/.env", + "echo AXIOM_API_KEY={$server->settings->logdrain_axiom_api_key} >> $config_path/.env", + ]; + } + $restart_command = [ + "echo 'Stopping old Fluent Bit'", + "cd $config_path && docker rm -f coolify-log-drain || true", + "echo 'Starting Fluent Bit'", + "cd $config_path && docker compose up -d --remove-orphans", + ]; + $command = array_merge($command, $add_envs_command, $restart_command); + return instant_remote_process($command, $server); + } catch (\Throwable $e) { + return handleError($e); + } + } +} diff --git a/app/Console/Commands/Init.php b/app/Console/Commands/Init.php index e0081d0d5..1bb4aa055 100644 --- a/app/Console/Commands/Init.php +++ b/app/Console/Commands/Init.php @@ -3,9 +3,11 @@ namespace App\Console\Commands; use App\Enums\ApplicationDeploymentStatus; +use App\Jobs\CleanupHelperContainersJob; use App\Models\Application; use App\Models\ApplicationDeploymentQueue; use App\Models\InstanceSettings; +use App\Models\Server; use App\Models\Service; use App\Models\ServiceApplication; use App\Models\ServiceDatabase; @@ -32,6 +34,16 @@ public function handle() $this->cleanup_ssh(); } $this->cleanup_in_progress_application_deployments(); + $this->cleanup_stucked_helper_containers(); + } + private function cleanup_stucked_helper_containers() { + $servers = Server::all(); + foreach ($servers as $server) { + if ($server->isFunctional()) { + CleanupHelperContainersJob::dispatch($server); + } + } + } private function alive() { diff --git a/app/Http/Livewire/Server/LogDrains.php b/app/Http/Livewire/Server/LogDrains.php new file mode 100644 index 000000000..cb7a5138a --- /dev/null +++ b/app/Http/Livewire/Server/LogDrains.php @@ -0,0 +1,138 @@ + 'required|boolean', + 'server.settings.logdrain_newrelic_license_key' => 'required|string', + 'server.settings.logdrain_newrelic_base_uri' => 'required|string', + 'server.settings.is_logdrain_highlight_enabled' => 'required|boolean', + 'server.settings.logdrain_highlight_project_id' => 'required|string', + 'server.settings.is_logdrain_axiom_enabled' => 'required|boolean', + 'server.settings.logdrain_axiom_dataset_name' => 'required|string', + 'server.settings.logdrain_axiom_api_key' => 'required|string', + ]; + protected $validationAttributes = [ + 'server.settings.is_logdrain_newrelic_enabled' => 'New Relic log drain', + 'server.settings.logdrain_newrelic_license_key' => 'New Relic license key', + 'server.settings.logdrain_newrelic_base_uri' => 'New Relic base URI', + 'server.settings.is_logdrain_highlight_enabled' => 'Highlight log drain', + 'server.settings.logdrain_highlight_project_id' => 'Highlight project ID', + 'server.settings.is_logdrain_axiom_enabled' => 'Axiom log drain', + 'server.settings.logdrain_axiom_dataset_name' => 'Axiom dataset name', + 'server.settings.logdrain_axiom_api_key' => 'Axiom API key', + ]; + + public function mount() + { + $this->parameters = get_route_parameters(); + try { + $server = Server::ownedByCurrentTeam(['name', 'description', 'ip', 'port', 'user', 'proxy'])->whereUuid(request()->server_uuid)->first(); + if (is_null($server)) { + return redirect()->route('server.all'); + } + $this->server = $server; + } catch (\Throwable $e) { + return handleError($e, $this); + } + } + public function configureLogDrain() + { + try { + if ($this->server->settings->is_logdrain_newrelic_enabled) { + $this->server->logDrain('newrelic'); + } else if ($this->server->settings->is_logdrain_highlight_enabled) { + $this->server->logDrain('highlight'); + } else if ($this->server->settings->is_logdrain_axiom_enabled) { + $this->server->logDrain('axiom'); + } else { + $this->server->logDrain('none'); + $this->emit('serverRefresh'); + $this->emit('success', 'Log drain service stopped.'); + return; + } + $this->emit('serverRefresh'); + $this->emit('success', 'Log drain service started successfully.'); + } catch (\Throwable $e) { + return handleError($e, $this); + } + } + public function instantSave(string $type) + { + try { + $ok = $this->submit($type); + if (!$ok) { + return; + } + $this->configureLogDrain(); + } catch (\Throwable $e) { + return handleError($e, $this); + } + } + public function submit(string $type) + { + try { + $this->resetErrorBag(); + if ($type === 'newrelic') { + $this->validate([ + 'server.settings.is_logdrain_newrelic_enabled' => 'required|boolean', + 'server.settings.logdrain_newrelic_license_key' => 'required|string', + 'server.settings.logdrain_newrelic_base_uri' => 'required|string', + ]); + $this->server->settings->update([ + 'is_logdrain_highlight_enabled' => false, + 'is_logdrain_axiom_enabled' => false, + ]); + } else if ($type === 'highlight') { + $this->validate([ + 'server.settings.is_logdrain_highlight_enabled' => 'required|boolean', + 'server.settings.logdrain_highlight_project_id' => 'required|string', + ]); + $this->server->settings->update([ + 'is_logdrain_newrelic_enabled' => false, + 'is_logdrain_axiom_enabled' => false, + ]); + } else if ($type === 'axiom') { + $this->validate([ + 'server.settings.is_logdrain_axiom_enabled' => 'required|boolean', + 'server.settings.logdrain_axiom_dataset_name' => 'required|string', + 'server.settings.logdrain_axiom_api_key' => 'required|string', + ]); + $this->server->settings->update([ + 'is_logdrain_newrelic_enabled' => false, + 'is_logdrain_highlight_enabled' => false, + ]); + } + $this->server->settings->save(); + $this->emit('success', 'Settings saved successfully.'); + return true; + } catch (\Throwable $e) { + if ($type === 'newrelic') { + $this->server->settings->update([ + 'is_logdrain_newrelic_enabled' => false, + ]); + } else if ($type === 'highlight') { + $this->server->settings->update([ + 'is_logdrain_highlight_enabled' => false, + ]); + } else if ($type === 'axiom') { + $this->server->settings->update([ + 'is_logdrain_axiom_enabled' => false, + ]); + } + handleError($e, $this); + return false; + } + } + public function render() + { + return view('livewire.server.log-drains'); + } +} diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 83674e815..25c6c879c 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -498,7 +498,7 @@ private function health_check() if ($this->full_healthcheck_url) { $this->execute_remote_command( [ - "echo 'Healthcheck URL inside your container: {$this->full_healthcheck_url}'" + "echo 'Healthcheck URL (inside the container): {$this->full_healthcheck_url}'" ] ); } @@ -837,13 +837,6 @@ private function generate_compose_file() 'networks' => [ $this->destination->network, ], - // 'logging' => [ - // 'driver' => 'fluentd', - // 'options' => [ - // 'fluentd-async' => 'true', - // 'tag' => $this->application->name . '-' . $this->application->uuid - // ] - // ], 'healthcheck' => [ 'test' => [ 'CMD-SHELL', @@ -871,6 +864,16 @@ private function generate_compose_file() ] ] ]; + if ($this->server->isDrainLogActivated()) { + $docker_compose['services'][$this->container_name]['logging'] = [ + 'driver' => 'fluentd', + 'options' => [ + 'fluentd-address' => "tcp://127.0.0.1:24224", + 'fluentd-async' => "true", + 'fluentd-sub-second-precision' => "true", + ] + ]; + } if ($this->application->isHealthcheckDisabled()) { data_forget($docker_compose, 'services.' . $this->container_name . '.healthcheck'); } @@ -1019,6 +1022,10 @@ private function build_image() listen [::]:80; server_name localhost; + // real_ip_header X-Forwarded-For; + // proxy_set_header X-Real-IP \$remote_addr; + // proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + location / { root /usr/share/nginx/html; index index.html; diff --git a/app/Jobs/CleanupHelperContainersJob.php b/app/Jobs/CleanupHelperContainersJob.php new file mode 100644 index 000000000..5c26ca930 --- /dev/null +++ b/app/Jobs/CleanupHelperContainersJob.php @@ -0,0 +1,40 @@ +server->name); + $containers = instant_remote_process(['docker container ps --filter "ancestor=ghcr.io/coollabsio/coolify-helper:next" --filter "ancestor=ghcr.io/coollabsio/coolify-helper:latest" --format \'{{json .}}\''], $this->server, false); + $containers = format_docker_command_output_to_json($containers); + if ($containers->count() > 0) { + foreach ($containers as $container) { + $containerId = data_get($container,'ID'); + ray('Removing container ' . $containerId); + instant_remote_process(['docker container rm -f ' . $containerId], $this->server, false); + } + } + } catch (\Throwable $e) { + send_internal_notification('CleanupHelperContainersJob failed with error: ' . $e->getMessage()); + ray($e->getMessage()); + } + } +} diff --git a/app/Models/Server.php b/app/Models/Server.php index d1b11a080..788808430 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -2,6 +2,8 @@ namespace App\Models; +use App\Actions\Server\InstallLogDrain; +use App\Actions\Server\InstallNewRelic; use App\Enums\ProxyStatus; use App\Enums\ProxyTypes; use App\Notifications\Server\Revived; @@ -59,6 +61,8 @@ protected static function booted() public $casts = [ 'proxy' => SchemalessAttributes::class, + 'logdrain_axiom_api_key' => 'encrypted', + 'logdrain_newrelic_license_key' => 'encrypted', ]; protected $schemalessAttributes = [ 'proxy', @@ -296,10 +300,17 @@ public function isProxyShouldRun() // } return true; } + public function logDrain($type) + { + InstallLogDrain::run($this, $type); + } public function isFunctional() { return $this->settings->is_reachable && $this->settings->is_usable; } + public function isDrainLogActivated() { + return $this->settings->is_logdrain_newrelic_enabled || $this->settings->is_logdrain_highlight_enabled || $this->settings->is_logdrain_axiom_enabled; + } public function validateConnection() { $uptime = instant_remote_process(['uptime'], $this, false); diff --git a/app/Models/Service.php b/app/Models/Service.php index 320d53ded..76c88eb85 100644 --- a/app/Models/Service.php +++ b/app/Models/Service.php @@ -797,6 +797,16 @@ public function parse(bool $isNew = false): Collection $serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik($this->uuid, $fqdns, true)); } } + if ($this->server->isDrainLogActivated()) { + data_set($service, 'logging', [ + 'driver' => 'fluentd', + 'options' => [ + 'fluentd-address' => "tcp://127.0.0.1:24224", + 'fluentd-async' => "true", + 'fluentd-sub-second-precision' => "true", + ] + ]); + } data_set($service, 'labels', $serviceLabels->toArray()); data_forget($service, 'is_database'); data_set($service, 'restart', RESTART_MODE); diff --git a/bootstrap/helpers/docker.php b/bootstrap/helpers/docker.php index 42ea0f9bc..36f8733b3 100644 --- a/bootstrap/helpers/docker.php +++ b/bootstrap/helpers/docker.php @@ -10,7 +10,6 @@ function getCurrentApplicationContainerStatus(Server $server, int $id, ?int $pullRequestId = null): Collection { - ray($id, $pullRequestId); $containers = collect([]); $containers = instant_remote_process(["docker ps -a --filter='label=coolify.applicationId={$id}' --format '{{json .}}' "], $server); $containers = format_docker_command_output_to_json($containers); @@ -26,7 +25,6 @@ function getCurrentApplicationContainerStatus(Server $server, int $id, ?int $pul return null; }); $containers = $containers->filter(); - ray($containers); return $containers; } diff --git a/config/sentry.php b/config/sentry.php index 2de0f59df..b66785b50 100644 --- a/config/sentry.php +++ b/config/sentry.php @@ -7,7 +7,7 @@ // The release version of your application // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')) - 'release' => '4.0.0-beta.138', + 'release' => '4.0.0-beta.139', // When left empty or `null` the Laravel environment will be used 'environment' => config('app.env'), diff --git a/config/version.php b/config/version.php index 8915456f9..085941feb 100644 --- a/config/version.php +++ b/config/version.php @@ -1,3 +1,3 @@ boolean('is_logdrain_newrelic_enabled')->default(false); + $table->string('logdrain_newrelic_license_key')->nullable(); + $table->string('logdrain_newrelic_base_uri')->nullable(); + + $table->boolean('is_logdrain_highlight_enabled')->default(false); + $table->string('logdrain_highlight_project_id')->nullable(); + + $table->boolean('is_logdrain_axiom_enabled')->default(false); + $table->string('logdrain_axiom_dataset_name')->nullable(); + $table->string('logdrain_axiom_api_key')->nullable(); + + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('server_settings', function (Blueprint $table) { + $table->dropColumn('is_logdrain_newrelic_enabled'); + $table->dropColumn('logdrain_newrelic_license_key'); + $table->dropColumn('logdrain_newrelic_base_uri'); + + $table->dropColumn('is_logdrain_highlight_enabled'); + $table->dropColumn('logdrain_highlight_project_id'); + + $table->dropColumn('is_logdrain_axiom_enabled'); + $table->dropColumn('logdrain_axiom_dataset_name'); + $table->dropColumn('logdrain_axiom_api_key'); + }); + } +}; diff --git a/examples/fluent-bit/fluent-bit.conf b/examples/fluent-bit/fluent-bit.conf deleted file mode 100644 index 5d10f559a..000000000 --- a/examples/fluent-bit/fluent-bit.conf +++ /dev/null @@ -1,16 +0,0 @@ -[SERVICE] - Flush 1 - Daemon off -[INPUT] - Name forward - Buffer_Chunk_Size 1M - Buffer_Max_Size 6M -# [OUTPUT] -# Name nrlogs -# Match * -# license_key ${LICENSE_KEY} -# base_uri https://log-api.eu.newrelic.com/log/v1 - -[OUTPUT] - Name stdout - Match * diff --git a/examples/fluent-bit/fluent-bit.yaml b/examples/fluent-bit/fluent-bit.yaml deleted file mode 100644 index ca573635e..000000000 --- a/examples/fluent-bit/fluent-bit.yaml +++ /dev/null @@ -1,9 +0,0 @@ -version: '3' -services: - coolify-fluent-bit: - image: cr.fluentbit.io/fluent/fluent-bit:2.0 - command: -c /fluent-bit.conf - volumes: - - ./fluent-bit.conf:/fluent-bit.conf - ports: - - 24224:24224 diff --git a/examples/newrelic.yaml b/examples/newrelic.yaml deleted file mode 100644 index 40bd5b0f2..000000000 --- a/examples/newrelic.yaml +++ /dev/null @@ -1,21 +0,0 @@ -version: '3' -services: - newrelic-infra: - container_name: newrelic-infra - image: newrelic/infrastructure:latest - networks: - - coolify - cap_add: - - SYS_PTRACE - privileged: true - pid: host - volumes: - - "/:/host:ro" - - "/var/run/docker.sock:/var/run/docker.sock" - - "newrelic-infra:/etc/newrelic-infra" - environment: - - NRIA_LICENSE_KEY=${NRIA_LICENSE_KEY} - - NRIA_DISPLAY_NAME=${HOSTNAME} - -networks: - coolify: diff --git a/examples/otl/config.yaml b/examples/otl/config.yaml deleted file mode 100644 index a1b8b7ec4..000000000 --- a/examples/otl/config.yaml +++ /dev/null @@ -1,34 +0,0 @@ -receivers: - hostmetrics: - collection_interval: 5s - scrapers: - cpu: - metrics: - system.cpu.utilization: - enabled: true -processors: - resourcedetection: - detectors: [env, system] - system: - hostname_sources: ["os"] - resource_attributes: - host.id: - enabled: true - batch: - memory_limiter: - check_interval: 1s - limit_mib: 1000 - spike_limit_mib: 200 -exporters: - debug: - verbosity: detailed - otlp: - endpoint: ${OTLP_ENDPOINT} - headers: - api-key: ${OTLP_API_KEY} -service: - pipelines: - metrics: - receivers: [hostmetrics] - processors: [memory_limiter, resourcedetection, batch] - exporters: [debug, otlp] diff --git a/resources/views/components/server/navbar.blade.php b/resources/views/components/server/navbar.blade.php index fbbdc545b..56b11ef78 100644 --- a/resources/views/components/server/navbar.blade.php +++ b/resources/views/components/server/navbar.blade.php @@ -32,6 +32,12 @@ ]) }}"> + + +

diff --git a/resources/views/livewire/server/log-drains.blade.php b/resources/views/livewire/server/log-drains.blade.php new file mode 100644 index 000000000..60f51665c --- /dev/null +++ b/resources/views/livewire/server/log-drains.blade.php @@ -0,0 +1,66 @@ +
+ +

Log Drains

+
Sends resource logs to external services.
+
+
+
+

New Relic

+
+ +
+
+
+ + +
+
+
+ + Save + +
+
+ {{--

Highlight.io

+
+ +
+
+
+
+ +
+
+
+ + Save + +
+
--}} +

Axiom

+
+ +
+
+
+
+ + +
+
+
+ + Save + +
+
+
+
+
diff --git a/routes/web.php b/routes/web.php index 54226ca89..6ac0d578c 100644 --- a/routes/web.php +++ b/routes/web.php @@ -16,6 +16,7 @@ use App\Http\Livewire\Server\All; use App\Http\Livewire\Server\Create; use App\Http\Livewire\Server\Destination\Show as DestinationShow; +use App\Http\Livewire\Server\LogDrains; use App\Http\Livewire\Server\PrivateKey\Show as PrivateKeyShow; use App\Http\Livewire\Server\Proxy\Show as ProxyShow; use App\Http\Livewire\Server\Proxy\Logs as ProxyLogs; @@ -130,6 +131,7 @@ Route::get('/server/{server_uuid}/proxy/logs', ProxyLogs::class)->name('server.proxy.logs'); Route::get('/server/{server_uuid}/private-key', PrivateKeyShow::class)->name('server.private-key'); Route::get('/server/{server_uuid}/destinations', DestinationShow::class)->name('server.destinations'); + Route::get('/server/{server_uuid}/log-drains', LogDrains::class)->name('server.log-drains'); }); diff --git a/versions.json b/versions.json index 26e16c15f..2dc7c1e54 100644 --- a/versions.json +++ b/versions.json @@ -4,7 +4,7 @@ "version": "3.12.36" }, "v4": { - "version": "4.0.0-beta.138" + "version": "4.0.0-beta.139" } } }