Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Telegram Logger Laravel Package

marekmiklusek/telegram-logger

marekmiklusek/telegram-logger adds a Laravel logging channel that sends application logs to Telegram chats via the Bot API. Configure the bot token, chat ID, and minimum log level to receive real-time error alerts and monitor events directly in Telegram.

View on GitHub
Deep Wiki
Context7

Getting Started

The package is a Monolog handler that ships log records to a Telegram chat via the Bot API. v2.0.0 is a delivery-pipeline rewrite with no public API change, so the integration surface is still: publish config → set credentials → point a logging channel at it.

1. Check your platform first. v2 requires PHP 8.4+ and Laravel 13+. On PHP 8.2/8.3 or Laravel 12, Composer will resolve 1.x — that is expected and not a broken update. Confirm before you plan the upgrade:

php -v && php artisan --version
composer require marekmiklusek/telegram-logger:^2.0

2. Publish the config (mandatory on upgrade — the new keys live here):

php artisan vendor:publish --tag=telegram-logger-config --force

An existing published config from 1.x still works (new settings fall back to safe defaults), but you lose flood protection, redaction and throw_on_failure tuning until you republish. Republish, then diff the file against your VCS history.

3. Set credentials in .env. Every setting is now env-configurable; the minimum is the bot token and target chat:

TELEGRAM_LOGGER_BOT_TOKEN=123456:ABC...
TELEGRAM_LOGGER_CHAT_ID=-1001234567890
TELEGRAM_LOGGER_LEVEL=error

4. Wire it into config/logging.php as a dedicated channel rather than replacing your stack driver:

'telegram' => [
    'driver'  => 'telegram',
    'level'   => env('TELEGRAM_LOGGER_LEVEL', 'error'),
],

5. First use case: reporting production exceptions. In bootstrap/app.php (Laravel 11+ style) or your Handler, send to the Telegram channel explicitly so the channel only fires on real incidents:

->withExceptions(function (Exceptions $exceptions) {
    $exceptions->report(function (Throwable $e) {
        Log::channel('telegram')->error($e->getMessage(), [
            'exception' => $e::class,
            'user_id'   => auth()->id(),
            'url'       => request()->fullUrl(),
        ]);
    });
})

You should see the new four-line layout in the chat:

❌ ERROR | Shop | production
Payment gateway timed out after 30s

📍 app/Http/Controllers/CheckoutController.php:87
🕑 16:41:13

{
    "order_id": 4711,
    "gateway": "stripe"
}

The heading carries level | app.name | environment, which is what makes a shared chat readable when several apps post into it.

Implementation Patterns

Channel-per-severity, not channel-per-app. Because the level is in the heading, one bot can serve several channels at different thresholds. Keep telegram for error and add a noisier telegram-alerts channel at warning if you want a second chat or a second token.

Treat context as the payload, not an afterthought. Context is rendered as syntax-highlighted JSON, so scalar bags beat string concatenation:

Log::channel('telegram')->error('Import failed', [
    'import_id'  => $import->id,
    'tenant'     => $import->tenant->slug,
    'rows_done'  => $import->processed,
    'exception'  => $e::class,   // class name is shown alongside the message
]);

Nested arrays render fine and are redacted recursively.

Redaction belongs in config, not in every call site. Put your organisation's sensitive keys in redact_keys once (password, token, authorization, and your own domain keys such as api_key, iban, card_number). Values are replaced with [REDACTED] at any nesting depth — this is the cheapest compliance win in the release, and it means developers can log a whole request payload without auditing it first.

Flood protection is your incident responder. A crash loop can turn one bug into thousands of Bot API calls. Set both knobs in production:

'dedupe_seconds'  => 60,   // identical message suppressed within the window
'max_per_minute'  => 15,   // hard ceiling on messages leaving the app

Design around them: dedupe is on identical rendered messages, so include a varying field (order id, request id) when you genuinely want every occurrence, and omit it when you want the loop collapsed. If you need a summary after suppression, log the counter from a scheduled command rather than fighting the dedupe window.

Two-tier logging for local vs production. Use throw_on_failure locally so a bad token or wrong chat id fails loudly during development, and leave it off in production where a logging failure must never take a request down:

// config/logging.php
'throw_on_failure' => env('APP_ENV') === 'local',

Route through a proxy or self-hosted Bot API with api_url — the practical reason is outbound network policy (corporate egress, a regional Bot API mirror, or a test double in CI). Point it at a local HTTP stub in your test suite to assert on the payload without touching Telegram.

Tune base_path when logs look truncated. File references are now path:line relative to the application root. If you deploy from a release directory (e.g. /var/www/releases/20240612/), set base_path so the stripped prefix matches the real document root rather than the default.

Log from jobs and commands, not from listeners that can re-enter. Because delivery now reads the API response, a failure is visible and retried-ish (see the plain-text retry), but a handler that logs inside a log listener is still a loop waiting to happen. Keep the channel out of the default stack driver so Log::error() elsewhere doesn't fan out to Telegram unless you intend it.

Audit what used to be silent. Delivery failures were previously indistinguishable from success — no response was read. After the upgrade, run a deliberate smoke test (invalid token, then valid) and confirm you now see the error path rather than an empty chat with a green dashboard.

Gotchas and Tips

  • On PHP 8.2/8.3 or Laravel 12 you cannot install v2. Composer silently offering 1.x is the documented behaviour, not a resolution bug. Pin ^2.0 explicitly so the constraint is visible in review, and check composer why-not if it refuses.
  • Republish the config before debugging anything else. A 1.x config throws InvalidArgumentException on every log call in some versions — that bug is fixed — but the practical consequence is you can't trust pre-republish behaviour. --force overwrites: commit first.
  • An invalid level now fails at boot. That is deliberate (previously it sent nothing, silently). It also means a typo in .env takes the app down on deploy rather than in a log line — validate the value in CI, and keep it on a channel rather than the default stack driver.
  • The 4096-character limit is characters, not bytes, and truncation is now multibyte-safe. Long stack traces used to be dropped entirely because truncation split a UTF-8 sequence or a Markdown entity and Telegram rejected the whole message. If you had "missing" messages in 1.x, they were almost certainly over-long payloads — check whether you still need to trim traces yourself.
  • Automatic plain-text retry is a safety net, not a formatting guarantee. On HTTP 400 the message is resent unformatted, so you get the content but lose bold/code and the visual structure. Frequent silent downgrades usually mean an unescaped character in a custom context value — backslash escaping for MarkdownV2 (the App\Models\User case) was broken and is fixed, so re-test any hand-rolled escaping.
  • Invalid UTF-8 in context used to drop the whole context block. Bytes are now replaced before escaping while valid characters and emoji survive. If you were seeing context vanish on binary-ish data, that is fixed — but sanitising at the source is still cheaper than relying on substitution.
  • allow_url_fopen must be enabled. It previously failed silently; it now throws with an actionable message. On hardened shared hosts, prefer setting api_url to a reachable endpoint over arguing with the flag, and confirm the function is available in the same PHP-FPM context your queue workers use.
  • Flood protection needs a cache. With no cache available both dedupe_seconds and max_per_minute are skipped by design so logging never stops — meaning a cache outage quietly disables your rate limiting. Use a shared cache store (Redis) if multiple servers log to the same chat, or the per-minute cap becomes per-server.
  • Messages now go out as POST, not GET. That removes log content from proxy and access logs — but if you had a WAF rule or egress allowlist tuned to GET query strings, verify the Bot API host is still permitted.
  • app.name participates in the payload size. A very long application name could push a message past the limit; that is fixed, but keep APP_NAME short anyway since it appears on every single message in a shared chat.
  • Exception class names are now shown. Good for triage, but it means messages containing a fully-qualified class name are longer — relevant if you also run an external truncation step.
  • Extension points remain config-level. There is no public API change in v2, so anything you built against the 1.x service/handler signature still compiles; if you need custom transport behaviour, use api_url and a proxy rather than patching the package.
  • Testing: the package's own suite is 144 tests with 100% code and type coverage, PHPStan at max, and a CI matrix over PHP 8.4/8.5. Mirror that discipline in your own assertions — point api_url at a local stub and assert the escaped body, rather than mocking the handler internals.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect