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**.