Send your Laravel/Lumen application logs straight to a Google Chat (Google Workspace) space via incoming webhooks.
theriddleofenigma/laravel-google-chat-log is a Laravel package for send your laravel/lumen application logs straight to a google chat (google workspace) space via incoming webhooks..
It currently has 24 GitHub stars and 86.125 downloads on Packagist (latest version v3.2.0).
Install it with composer require theriddleofenigma/laravel-google-chat-log.
Discover more Laravel packages by theriddleofenigma
or browse all Laravel packages to compare alternatives.
Last updated
♥ Made with <love/> And I love <code/>
Send your Laravel/Lumen application logs straight to a Google Chat space [Google Workspace, formerly GSuite] via incoming webhooks.
8.2, 8.3, 8.4 or 8.5 (Laravel 13 requires PHP 8.3+; Laravel 11
is only tested on PHP 8.2-8.4)illuminate/support ^11.0|^12.0|^13.0)For Laravel 10 use
^2.x. For Laravel 9 or lower use^1.x.
composer require theriddleofenigma/laravel-google-chat-log
The package ships with Laravel package auto-discovery, so the service provider
is registered for you and it automatically adds a google-chat logging
channel. In most cases the only thing you need to do is set the webhook url:
LOG_GOOGLE_CHAT_WEBHOOK_URL=https://chat.googleapis.com/v1/spaces/XXXX/messages?key=...&token=...
Then log to the channel:
use Illuminate\Support\Facades\Log;
Log::channel('google-chat')->error('Something went wrong');
To route your default log stack through Google Chat, add google-chat to the
stack channel in config/logging.php, or set LOG_CHANNEL=google-chat.
The channel is registered with sensible defaults, but anything you define in
your application's config/logging.php always takes precedence. You can
publish the channel definition to tweak it:
php artisan vendor:publish --tag=google-chat-log-config
Or define it manually in the channels array of config/logging.php:
'google-chat' => [
'driver' => 'monolog',
'handler' => \Enigma\GoogleChatHandler::class,
'url' => env('LOG_GOOGLE_CHAT_WEBHOOK_URL'),
'level' => env('LOG_GOOGLE_CHAT_LEVEL', env('LOG_LEVEL', 'debug')),
'notify_users' => [
'default' => env('LOG_GOOGLE_CHAT_NOTIFY_USER_ID_DEFAULT'),
'emergency' => env('LOG_GOOGLE_CHAT_NOTIFY_USER_ID_EMERGENCY'),
'alert' => env('LOG_GOOGLE_CHAT_NOTIFY_USER_ID_ALERT'),
'critical' => env('LOG_GOOGLE_CHAT_NOTIFY_USER_ID_CRITICAL'),
'error' => env('LOG_GOOGLE_CHAT_NOTIFY_USER_ID_ERROR'),
'warning' => env('LOG_GOOGLE_CHAT_NOTIFY_USER_ID_WARNING'),
'notice' => env('LOG_GOOGLE_CHAT_NOTIFY_USER_ID_NOTICE'),
'info' => env('LOG_GOOGLE_CHAT_NOTIFY_USER_ID_INFO'),
'debug' => env('LOG_GOOGLE_CHAT_NOTIFY_USER_ID_DEBUG'),
],
],
Lumen: make sure
$app->withFacades();is uncommented inbootstrap/app.php, and registerEnigma\GoogleChatLogServiceProvider.
You can provide the eight logging levels defined in the
RFC 5424 specification: emergency,
alert, critical, error, warning, notice, info, and debug.
Set multiple Google Chat webhook urls as a comma separated value for the
LOG_GOOGLE_CHAT_WEBHOOK_URL env variable (or as an array in the channel
config) and every space receives the log.
Since 3.2 every channel can have its own webhook, level, mentions and options.
The recommended way is a custom driver channel built by
Enigma\GoogleChatLogger. Laravel hands it the whole channel config, so every
key is read from the channel itself:
'google-chat-ops' => [
'driver' => 'custom',
'via' => \Enigma\GoogleChatLogger::class,
'name' => 'google-chat-ops',
'url' => env('LOG_GOOGLE_CHAT_OPS_WEBHOOK_URL'),
'level' => 'critical',
'notify_users' => ['default' => 'all'],
],
name is optional. As with any Laravel channel it becomes the Monolog channel
name in the message text (the environment name when omitted), and it is the
name to use with additionalLogsFor(). Laravel
does not tell a custom factory the channel's key, so a channel without name
is anonymous: additionalLogsFor() can not target it, and it never picks up
hooks registered for google-chat (the additional_logs class and the global
closure still apply). The
factory builds the channel like Laravel's monolog driver: same default
formatter, and formatter, processors and action_level work as usual.
With the monolog driver, Laravel only passes the handler the keys under
with, so tell it which channel config to read:
'google-chat-ops' => [
'driver' => 'monolog',
'handler' => \Enigma\GoogleChatHandler::class,
'with' => ['channel' => 'google-chat-ops'],
'url' => env('LOG_GOOGLE_CHAT_OPS_WEBHOOK_URL'),
'level' => 'critical',
'notify_users' => ['default' => 'all'],
],
A monolog channel without with.channel reads logging.channels.google-chat,
as in earlier releases, even if it sets its own url. php artisan google-chat-log:test --channel=<name> warns about that case. Keys missing from
a channel use the defaults listed in
Configuration reference, so a channel only needs the
keys it changes.
For on-demand channels, pass the whole config instead:
Log::build([
'driver' => 'monolog',
'handler' => \Enigma\GoogleChatHandler::class,
'with' => ['config' => ['url' => 'https://chat.googleapis.com/v1/spaces/...']],
])->error('Something went wrong');
Since 3.2, a failed delivery never throws out of the logger: connection errors, timeouts and rejected responses are caught, so logging can not break the request that logged. Everything below is opt-in; the defaults behave exactly like 3.1.
LOG_GOOGLE_CHAT_FALLBACK_CHANNEL=daily to get a
short report (masked webhook, HTTP status or error) whenever a message can
not be delivered. Without it, failures stay silent. Webhook urls are always
masked as spaces/{SPACE_ID}/messages?key=***&token=***.LOG_GOOGLE_CHAT_TIMEOUT and LOG_GOOGLE_CHAT_CONNECT_TIMEOUT
(seconds). Unset, Laravel's HTTP client defaults apply (30s / 10s), so a slow
webhook can stall a request for that long. 5 and 2 are good values.LOG_GOOGLE_CHAT_RETRIES=2 retries 429 and 5xx responses
only, with exponential backoff and jitter (retry_delay, default 500 ms,
doubling per attempt) and honours Google's Retry-After header up to
retry_max_delay (default 10000 ms), which caps every wait. Retries run
synchronously, inside the request that logged.LOG_GOOGLE_CHAT_ENABLED=false turns the
channel off; LOG_GOOGLE_CHAT_ENVIRONMENTS=production,staging limits it to
those environments.LOG_GOOGLE_CHAT_ON_MISSING_URL is throw by default (a
RuntimeException, as before), ignore to drop messages silently, or warn
to drop them and report once per process to the fallback channel.[truncated]. Smaller
messages are sent unchanged.php artisan google-chat-log:test
php artisan google-chat-log:test --channel=google-chat-ops --level=critical
The command sends a sample card to every webhook of the channel and explains the response:
| Status | Meaning |
|---|---|
| 200 | Delivered. |
| 403 | Incoming webhooks are probably disabled by your Workspace admin. |
| 404 | The webhook or the space was deleted. |
| 429 | The space's rate limit was hit. |
It exits with a non-zero code on any failure, and warns (but still sends) when
enabled or environments would block normal delivery.
@mentionNotify a specific user by setting the corresponding user id. Ids mapped under
LOG_GOOGLE_CHAT_NOTIFY_USER_ID_DEFAULT are notified for all log levels:
LOG_GOOGLE_CHAT_NOTIFY_USER_ID_DEFAULT=1234567890
To find a USER_ID, right-click the user icon of the person you want to
notify in Google Chat and select inspect. On the div element find the
data-member-id attribute — the id is the value in
data-member-id="user/human/{USER_ID}".
To notify everyone (@all), set LOG_GOOGLE_CHAT_NOTIFY_USER_ID_DEFAULT=all.
Multiple ids can be provided as a comma separated value, and each log level can
target different users via its matching LOG_GOOGLE_CHAT_NOTIFY_USER_ID_* env
variable.
Append extra key-value pairs to every Google Chat message by assigning a
closure to GoogleChatHandler::$additionalLogs. The closure receives the
current Monolog\LogRecord and must return an array:
use Enigma\GoogleChatHandler;
use Monolog\LogRecord;
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
GoogleChatHandler::$additionalLogs = function (LogRecord $record) {
return [
'tenant' => request()->user()?->tenant->name,
'request' => request()->fullUrl(),
];
};
}
}
Non-string values are JSON encoded automatically.
Since 3.2 a channel can have its own hook. Use an invokable class in the channel
config, which keeps php artisan config:cache working (closures can not be
cached):
// config/logging.php
'google-chat' => [
// ...
'additional_logs' => \App\Logging\ChatExtras::class,
],
// app/Logging/ChatExtras.php
class ChatExtras
{
public function __invoke(LogRecord $record): array
{
return ['tenant' => request()->user()?->tenant->name];
}
}
Or register a closure for one channel from a service provider:
GoogleChatHandler::additionalLogsFor('google-chat-ops', fn (LogRecord $record) => [
'queue' => $record->context['queue'] ?? null,
]);
For each channel the first match wins: the registered closure, then the
additional_logs class, then the global GoogleChatHandler::$additionalLogs.
All keys are optional. A missing key uses the default shown, which matches the behaviour of 3.1.
| Key | Env variable | Default | Since |
|---|---|---|---|
| url | LOG_GOOGLE_CHAT_WEBHOOK_URL | – (required) | 1.x |
| level | LOG_GOOGLE_CHAT_LEVEL, then LOG_LEVEL | debug | 3.2 (LOG_GOOGLE_CHAT_LEVEL) |
| notify_users.default | LOG_GOOGLE_CHAT_NOTIFY_USER_ID_DEFAULT | – | 1.x |
| notify_users.{level} | LOG_GOOGLE_CHAT_NOTIFY_USER_ID_{LEVEL} | – | 1.x |
| enabled | LOG_GOOGLE_CHAT_ENABLED | true | 3.2 |
| environments | LOG_GOOGLE_CHAT_ENVIRONMENTS | all | 3.2 |
| on_missing_url | LOG_GOOGLE_CHAT_ON_MISSING_URL | throw | 3.2 |
| timeout | LOG_GOOGLE_CHAT_TIMEOUT | Laravel default (30s) | 3.2 |
| connect_timeout | LOG_GOOGLE_CHAT_CONNECT_TIMEOUT | Laravel default (10s) | 3.2 |
| retries | LOG_GOOGLE_CHAT_RETRIES | 0 | 3.2 |
| retry_delay | – | 500 (ms) | 3.2 |
| retry_max_delay | – | 10000 (ms) | 3.2 |
| fallback_channel | LOG_GOOGLE_CHAT_FALLBACK_CHANNEL | none (silent) | 3.2 |
| additional_logs | – | none | 3.2 |
The throughput limit is the same on every Workspace plan. The plan only decides whether incoming webhooks are available at all.
| Account / plan | Incoming webhooks? | Notes |
|---|---|---|
| Personal Gmail account | No | Chat is free on personal accounts, but app webhooks are reserved for Workspace. |
| Workspace Business (Starter / Standard / Plus) | Yes, if the admin allows it | Google's webhook quickstart requires a Business or Enterprise account whose organization lets users add and use incoming webhooks. |
| Workspace Enterprise | Yes, if the admin allows it | Same throughput as Business. |
| Workspace Education / other editions | Verify | Not confirmed in Google's webhook quickstart. Test with php artisan google-chat-log:test. |
The admin setting is Admin console → Apps → Google Workspace → Google Chat →
Chat apps → "Allow users to add and use incoming webhooks" = On
(Google help). Only users in the
organization can create webhooks. A 403 from the test command usually means
this setting is off.
debug/info, or needs its own space.| Profile | Recommended settings |
|---|---|
| Hobby / single server | LOG_GOOGLE_CHAT_LEVEL=error, LOG_GOOGLE_CHAT_TIMEOUT=5, LOG_GOOGLE_CHAT_CONNECT_TIMEOUT=2, LOG_GOOGLE_CHAT_RETRIES=2. Keep daily in the stack next to google-chat. |
| Production, multiple servers | All of the above, plus LOG_GOOGLE_CHAT_FALLBACK_CHANNEL=daily. Use a separate channel (and space) per audience, see Multiple channels. |
| High traffic / incident storms | Treat Chat as alerting: LOG_GOOGLE_CHAT_LEVEL=critical or a dedicated space for critical alerts. Send full log volume to a real log store. |
| Local / CI | LOG_GOOGLE_CHAT_ENABLED=false, or LOG_GOOGLE_CHAT_ENVIRONMENTS=production, or LOG_GOOGLE_CHAT_ON_MISSING_URL=ignore. |
# Recommended production baseline (all opt-in in 3.x)
LOG_GOOGLE_CHAT_LEVEL=error
LOG_GOOGLE_CHAT_TIMEOUT=5
LOG_GOOGLE_CHAT_CONNECT_TIMEOUT=2
LOG_GOOGLE_CHAT_RETRIES=2
LOG_GOOGLE_CHAT_FALLBACK_CHANNEL=daily
The 4.0 release plans to make LOG_GOOGLE_CHAT_LEVEL=error, the 5s / 2s
timeouts and 2 retries the defaults. Setting them now gives you 4.0's
behaviour on 3.x.
composer test # run the PHPUnit suite
composer lint # check code style with Laravel Pint
composer format # fix code style automatically
The live suite in tests/Live sends real messages and is never part of
composer test. Point it at a dedicated scratch space, because it uses that
space's 1 request per second quota:
LOG_GOOGLE_CHAT_LIVE_WEBHOOK="https://chat.googleapis.com/v1/spaces/..." composer test:live
The Live GitHub workflow runs it nightly and on demand, using the
LOG_GOOGLE_CHAT_LIVE_WEBHOOK repository secret. It never runs on pull
requests.
Contributions are welcome — please read the contributing guide first. For security issues, see the security policy.
Copyright © Kumaravel
Laravel Google Chat Log is open-sourced software licensed under the MIT license.