evercodelab/hipchat-monolog-bundle
A Symfony bundle that connects Monolog to HipChat, letting your application send log records and alerts straight into HipChat rooms for real-time team visibility. Simple to configure and handy for monitoring.
Check the ground first. This bundle is a thin Symfony wrapper around Monolog's HipChatHandler, which was deprecated in Monolog 1.x and removed in Monolog 2.0. Before you composer require anything, open the repo's composer.json and confirm the constraints (symfony/symfony, monolog/monolog). If your app is on Symfony 4+ / Monolog 2+, expect a dependency conflict and plan accordingly — you will likely end up copying the handler config rather than installing the bundle.
Minimal install path (Symfony 2/3 era app):
composer require evercodelab/hipchat-monolog-bundle
Register it in app/AppKernel.php:
$bundles = [
// ...
new EvercodeLab\HipchatMonologBundle\EvercodeHipchatMonologBundle(),
];
Get credentials: in HipChat, a group admin creates an API token (Group admin → API → Create token) with send_notification scope, and you need the target room name or room ID.
First use case — page yourself on production errors. Wire it as a Monolog handler with a tight level so only real incidents reach the room:
# app/config/config_prod.yml
parameters:
hipchat_token: "%env(HIPCHAT_TOKEN)%"
hipchat_room: "%env(HIPCHAT_ROOM)%"
monolog:
handlers:
hipchat:
type: hipchat
token: "%hipchat_token%"
room: "%hipchat_room%"
level: error
bubble: true
Then verify end-to-end with a throwaway command rather than waiting for a real 500:
bin/console cache:clear --env=prod
# in a quick tinker/command: $logger->error('hipchat smoke test');
Where to look first: DependencyInjection/Configuration.php and DependencyInjection/EvercodeHipchatMonologExtension.php in the bundle — they are the real documentation for the config keys. Also run bin/console config:dump-reference evercode_hipchat_monolog (if the bundle exposes an extension alias) and bin/console debug:container monolog.handler to see how it registers.
1. Route by channel, not by level. Give noisy subsystems their own Monolog channel and let the HipChat handler subscribe only to the ones that matter to the room's audience:
monolog:
channels: ["payments", "security"]
handlers:
hipchat_security:
type: hipchat
token: "%hipchat_token%"
room: "security-alerts"
level: warning
channels: ["security"]
This is the single highest-leverage thing you can do: one room per team, one channel per domain, and nobody mutes the bot because of unrelated deprecation warnings.
2. Buffer context with fingers_crossed. A raw handler on every error means a stack trace with no request context. Nest it so the handler only fires on error+ but still carries the accumulated debug/info breadcrumbs:
monolog:
handlers:
buffer:
type: fingers_crossed
action_level: error
handler: hipchat
hipchat:
type: hipchat
token: "%hipchat_token%"
room: "%hipchat_room%"
level: debug
3. Format for the chat client, not for the log file. Default Monolog line formatter dumps multi-line traces that HipChat renders as an unreadable wall. Register a short formatter service and attach it:
services:
app.monolog.hipchat_formatter:
class: Monolog\Formatter\LineFormatter
arguments:
- "[%level_name%] %channel% | %message% | %context.app_version%"
- null
- true # allow inline line breaks
- true # ignore empty context
Trim %extra% (exception traces) out of the chat line entirely and put the trace ID in the message — the room is for triage, the log aggregator is for forensics.
4. Never let the notifier break the request. Wrap it so a HipChat outage or SSL failure doesn't turn a handled exception into a fatal one:
hipchat:
type: whatfailuregroup
members: [hipchat_real]
(or configure the handler service directly with setTimeout() / a short HTTP timeout). This is the difference between "alerts are down" and "the whole site is down".
5. Manual notifications on a dedicated logger. Inject the channel-specific logger where you want a human-facing message (deploy finished, queue backlog, failed job) rather than abusing error():
public function __construct(LoggerInterface $hipchatLogger) { ... }
$this->hipchatLogger->notice(sprintf('Deploy %s finished in %ds', $tag, $elapsed));
6. Environment gating. Keep the handler in config_prod.yml only; in config_dev.yml route to test/null so bin/console runs and test suites don't spam a shared room. A single stray functional test can generate hundreds of messages and get your token rate-limited.
HipChatHandler in 2.x. Treat this bundle as a migration source, not a destination: the day-to-day value is copying its DI wiring once, then pointing the same handler shape at SlackHandler, TelegramHandler, or a generic Webhook/Curl handler. Same architecture, supported code.fingers_crossed, deduplication (Symfony 5.2+ / a custom DeduplicationHandler), or a sampling wrapper. Consider a FingersCrossedHandler stop_buffering: false so you keep the first trace.bubble: false silently swallows. It stops propagation to subsequent handlers. If you set it for dedupe reasons, confirm your file/stderr handlers still receive the record.whatfailuregroup, that becomes your exception. Truncate the message explicitly in your formatter.parameters.yml. Use %env(HIPCHAT_TOKEN)% (or Symfony Secrets) and rotate it in HipChat's group admin UI; tokens are per-room-scoped, so use distinct tokens per environment if you can.Configuration.php for the authoritative list, then bin/console debug:config to confirm what actually got resolved — do not guess keys like notify/from and assume they work.curl the HipChat API directly with your token/room — if that fails, the bundle is irrelevant. (2) debug:container to confirm the handler service exists and is tagged monolog.logger. (3) Add a test handler alongside so you can see whether the record even reaches Monolog. (4) Check the Symfony profiler's Logs panel in dev.monolog.handler.hipchat) to add retry/backoff, or skip the bundle's config entirely and define the handler as a type: service Monolog handler with your own factory — usually the cleanest way to get off this bundle without a rewrite.How can I help you explore Laravel packages today?