Here's a **complete, final summary** of the working setup for **Log::Any → Dancer2 logging** with correct caller info, moved into its own package. --- ## Goal * Use `Log::Any` inside model classes (`$log->debug(...)`). * Forward those messages into the **current Dancer2 logger** (file/console/whatever). * Preserve the **real file and line** of the model call. * Keep all adapter setup in a separate package. --- ## 1️⃣ Logging Setup Package `lib/MyApp/LogSetup.pm` ```perl package MyApp::LogSetup; use strict; use warnings; use Dancer2 (); # load but don’t import the DSL use Log::Any::Adapter; sub init { Log::Any::Adapter->set( Capture => min_level => 'debug', to => sub { my ($method, $self, $format, @params) = @_; my $msg = @params ? sprintf($format, @params) : $format; # Find the first registered Dancer2 app my ($app) = @{ Dancer2->runner->apps } or return; # Skip Log::Any frames so caller points to your model code my (undef, $file, $line) = caller(3); $app->logger_engine->log( $method => $msg, caller => [$file, $line], ); }, ); } 1; ``` * `Dancer2->runner->apps` returns all running apps; we use the first for a single-app setup. * `caller(3)` skips the adapter + Log::Any wrappers so the model’s file/line is recorded. --- ## 2️⃣ Main Application `lib/MyApp.pm` ```perl package MyApp; use Dancer2; use MyApp::LogSetup; # Initialise Log::Any forwarding before routes MyApp::LogSetup::init(); get '/' => sub { "Hello World" }; 1; ``` Configure `config.yml` normally, e.g.: ```yaml logger: "file" log: "debug" engines: logger: file: filename: "/var/log/myapp.log" layout: "%m in %f line %l" ``` `%f` and `%l` will show the model file/line we supply. --- ## 3️⃣ Model Classes `lib/MyApp/Model/User.pm` ```perl package MyApp::Model::User; use strict; use warnings; use Log::Any '$log'; sub create { $log->debug("Creating a user"); # will log with this file/line ... } 1; ``` No Dancer2 dependency here — only `Log::Any`. --- ## 4️⃣ Behaviour * Any `$log->debug/info/warning/...` inside models is captured by the callback. * Messages are forwarded to the Dancer2 logger (File, Console, Syslog, etc.) exactly like native `debug`. * Log entries display the **model’s actual file and line number**, not the adapter. --- ### Notes & Tips * If you run **multiple Dancer2 apps** in one process, filter `Dancer2->runner->apps` by app name. * In unit tests, you can skip `MyApp::LogSetup::init()` and instead set `Log::Any::Adapter->set('Test')` to capture logs without Dancer2. * Adjust `caller(3)` if you add extra wrapper layers. --- This pattern keeps **models clean**, gives you **centralised logging setup**, and ensures all Log::Any messages appear in **Dancer2’s normal logs with correct origin information**.