view release on metacpan or search on metacpan
devdata/http_advent.perldancer.org_2018_16 view on Meta::CPAN
221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268use
MyJob::JobQueue;
use
MyJob::Log4p;
use
MyJob::Util::Logger;
my
$config
= MyJob::Config-
>
;new-
>
;config;
my
$hostconfig
= get_hostconfig();
my
$minion
= MyJob::JobQueue-
>
;new;
my
$worker
=
$minion
-
>
;runner-
>
;worker;
my
$log_eng
= MyJob::Log4p-
>
;new({ logger_name =
>
;
"Minion"
});
my
$logger
= MyJob::Util::Logger-
>
;new-
>
;logger(
$log_eng
);</pre>
<p>The above is mostly typical boilerplate
for
us. Read
our
configuration, and create a logger the worker can
use
.</p>
<p>Next,
when
a job is dequeued, we want to
log
that the worker picked up a job (needed
for
auditing purposes) and we alter the process name so
if
a process hangs, we know what that process
was attempting to run. If an unchecked exception occurs in a job, the worker will
catch
it and
log
it
for
us:</p>
<pre class=
"prettyprint"
>
$worker
-
>
;on( dequeue =
>
;
sub
(
$worker
,
$job
) {
my
$id
=
$job
-
>
;id;
my
$notes
=
$job
-
>
;info-
>
;{ notes };
my
$title
=
$notes
-
>
;{ title };
my
$guid
=
$notes
-
>
;{ guid };
$job
-
>
;on( spawn =
>
;
sub
(
$job
,
$pid
) {
$0 =
"$title $guid"
;
$logger
-
>
;info(
"$title: Created child process $pid for job $id by parent $$ - $guid"
);
});
$job
-
>
;on( failed =
>
;
sub
(
$job
,
$error
) {
chomp
$error
;
$logger
-
>
;error(
$error
);
});
});</pre>
<p>To help us
for
future capacity planning, we want
our
workers to
tell
us
if
they are running at peak capacity, so
log
when
this event occurs:</p>
<pre class=
"prettyprint"
>
$worker
-
>
;on( busy =
>
;
sub
(
$worker
) {
my
$max
=
$worker
-
>
;status-
>
;{ jobs };
$logger
-
>
;
log
(
"$0: Running at capacity (performing $max jobs)."
);
});</pre>
<p>Now, we apply the configuration (
read
below) to the worker. When the worker starts, it tells us information about how it was configured (this was really useful during development):</p>
<pre class=
"prettyprint"
>
my
$max_jobs
=
$hostconfig
-
>
;{ max_children };
my
@queues
= @{
$hostconfig
-
>
;{ queues }};
if
(
$minion
-
>
;has_invalid_queues(
@queues
) ){
"Invalid job queues specified: "
.
join
(
','
,
$minion
-
>
;get_invalid_queues(
@queues
) );
say
". Aborting!"
;
devdata/http_advent.perldancer.org_2018_16 view on Meta::CPAN
285286287288289290291292293294295296297298299300301302303304305
if
(
exists
$minion_config
-
>
;{
$hostname
}) {
return
$minion_config
-
>
;{
$hostname
};
}
else
{
return
$minion_config
-
>
;{
default
};
}
}</pre>
<h2><a name=
"monitoring_the_workers"
></a>Monitoring the Workers</h2>
<p>Our Minion dashboard was virtually identical to the one that
@preaction
posted in <a href=
"https://mojolicious.io/blog/2018/12/11/who-watches-the-minions/#section-2"
>Who Watches the Minions?</a>.
If you'd like to know more, I highly recommend reading his article.</p>
<h2><a name=
"outcome"
></a>Outcome</h2>
<p>Within about a two-week timespan, we went from having zero practical knowledge of Minion to having things up and running. We've made some refinements and improvements along the way, but the quick turnaround
is a true testament to the simplicity of working
with
Minion.</p>
<p>We now have all the necessary pieces in place to scale
our
XML rendering both horizontally and vertically: thanks to Minion, we can easily run XML jobs across multiple boxes, and can more efficiently run
more jobs concurrently on the same hardware as
before
. This setup allows us to grow as quickly as
our
customer base does.</p>
<h2><a name=
"author"
></a>Author</h2>
<p>This article
has
been written by Jason Crome (CromeDome)
for
the Perl Dancer
devdata/http_advent.perldancer.org_2018_17 view on Meta::CPAN
979899100101102103104105106107108109110111112113114115116117<h2><a name=
"inline_images"
></a>Inline images</h2>
<p>You can simply deliver emails
with
links to images, but usually email clients would not load them without user interaction.
It is possible though to attached the images to the email and reference them in the email body
with
a custom HTML tag:</p>
<pre class=
"prettyprint"
> email {
from =
>
;
'foo@perl.dance'
,
to =
>
;
'bar@perl.dance'
,
subject =
>
;
'Welcome to the dancefloor!'
,
body =
>
;
q{<p>Image embedded: <img src="cid:mycid"/></p>}
,
type =
>
;
'html'
,
attach =
>
; [ { Id =
>
;
'mycid'
, Path =
>
;
'/dancefloor/dcr-header-logo.png'
}],
multipart =
>
;
'related'
};</pre>
<h2><a name=
"providing_plain_text_part"
></a>Providing plain text part</h2>
<p><a href=
"https://metacpan.org/pod/HTML::FormatText::WithLinks"
>HTML::FormatText::WithLinks</a> makes it easy to provide a plain text version
of your HTML email:</p>
<pre class=
"prettyprint"
>
my
$html
= template
$template
,
$tokens
, { layout =
>
;
'email'
};
my
$f
= HTML::FormatText::WithLinks-
>
;new;
devdata/http_advent.perldancer.org_2018_19 view on Meta::CPAN
434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
<li><a class=
"feed"
href=
"/feed/2018"
>RSS</a></li>
</ul>
</li>
</ul>
</div>
<div id=
"content"
>
<div class=
"pod-document"
><h1><a name=
"logging_with_dancer2__logger__log4perl"
></a>Logging
with
Dancer2::Logger::Log4perl</h1>
<h2><a name=
"history"
></a>History</h2>
<p>It's late summer 2018, and there
has
been a hole in the logging ecosystem of Dancer2
for
some
time
now - we've been missing a Log4perl plugin. There are certainly a lot of great logging options
available (<code>Dancer2::Logger::LogAny</code>, <code>Dancer2::Logger::Syslog</code>, and <code>Dancer2::Logger::LogReport</code>
to name a few), and a couple of them even include appenders
for
Log4perl. In looking at
our
own
needs, however, they all seemed to be overkill.</p>
<p>At <code>
$work
</code>, everything is based on Log4perl (and, in
our
client-facing Java apps, Log4j), so
there
has
never been need
for
another logger. We'd been happily using <code>Dancer::Logger::Log4perl</code>
writing
our
own Log4perl plugin
for
Dancer2, we expanded
our
search beyond metacpan, and were
rewarded in doing so.</p>
<p>We stumbled across a project from Ryan Larscheidt and Jon Miner at the University of Wisconsin.
They wrote a Log4perl plugin
for
Dancer2
with
the intent of releasing it, but as their priorities
and projects shifted, it never made its way to CPAN. I used to go to MadMongers (Madison Perl Mongers),
so
before
long, I was able to track them down and they gave me their blessing to release it.</p>
<p>A little packaging and a few tests later, <code>Dancer2::Logger::Log4perl</code> was on its way to CPAN!</p>
<h2><a name=
"configuration"
></a>Configuration</h2>
<p>First, we need to create a basic Log4perl configuration. Create a new Dancer2 application (I'll call
mine <code>TestLog4perl</code>), and in the application directory create a <i>log4perl.conf</i> file
with
the following:</p>
<pre class=
"prettyprint"
>log4perl.rootLogger = DEBUG, LOG1
log4perl.appender.LOG1 = Log::Log4perl::Appender::File
log4perl.appender.LOG1.filename = logs/mylog.
log
log4perl.appender.LOG1.mode = append
log4perl.appender.LOG1.layout = Log::Log4perl::Layout::PatternLayout
log4perl.appender.LOG1.layout.ConversionPattern =
%d
%p
%m
%n
</pre>
<p>The first one defines
our
root application logger. The first parameter
after
the equals sign says
what is the minimum level we should
log
. Since we are saying the minimum
log
level should be <code>DEBUG</code>,
any messages from Dancer2 itself, and anything logged at the <code>core</code> level will be ignored.</p>
<p>After the minimum
log
level is a comma-separated list of appenders to
write
to. For now, we will create a
single appender named <code>LOG1</code> (we will see how to add a second appender below). This will
write
to a file in the <i>logs/</i> directory named <i>mylog.
log
</i>, using the <a href=
"https://metacpan.org/pod/Log::Log4perl::Appender::File"
>Log::Log4perl::Appender::File</a>
appender. When the app is started, it will append to an existing
log
file of that name (or create the file
if
necessary), and will
write
to it
with
a
format
specified by <a href=
"https://metacpan.org/pod/Log::Log4perl::Layout::PatternLayout"
>Log::Log4perl::Layout::PatternLayout</a>.</p>
<p>Each appender can have its own configuration directives. Please consult the pod
for
each
appender
for
a list
of its configuration parameters.</p>
<p>Next, we have to
tell
our
application that we are using <code>Dancer2::Logger::Log4perl</code> as
our
preferred logger.
Edit your <i>environments/development.yml</i> file, and comment out the <code>logger:
"console"
</code> line. Replace it
with
the following:</p>
<pre class=
"prettyprint"
>logger: log4perl
log
: core
engines:
logger:
log4perl:
config_file: log4perl.conf</pre>
<p>This tells Dancer2 to
use
<code>Dancer2::Logger::Log4perl</code> as its logging engine, and to
send
all levels of message to
it. Finally, Log4perl should look
for
its configuration file in the root application directory in a file called
<i>log4perl.conf</i>.</p>
<h2><a name=
"usage"
></a>Usage</h2>
<pre class=
"prettyprint"
>get
'/'
=
>
;
sub
{
debug
"I'M IN UR INDEX"
;
template
'index'
=
>
; {
'title'
=
>
;
'TestLog4perl'
};
};</pre>
<p>Start your application and visit <code>http://localhost:5000/</code>. You will see the following in your <i>logs/mylog.
log
</i> file:</p>
<pre class=
"prettyprint"
>2018/12/18 21:36:02 DEBUG I'M IN UR INDEX</pre>
<h2><a name=
"hey__i_can_t_see_my_log_messages_on_the_screen_"
></a>Hey, I can't see
my
log
messages on the screen!</h2>
<p>That
's because we didn'
t add a screen appender! With Log4perl, adding another appender is easy. Let's
add another section to
our
<i>log4perl.conf</i> file:</p>
<pre class=
"prettyprint"
>log4perl.appender.SCREEN = Log::Log4perl::Appender::Screen
log4perl.appender.SCREEN.stderr = 0
log4perl.appender.SCREEN.layout = Log::Log4perl::Layout::PatternLayout
log4perl.appender.SCREEN.layout.ConversionPattern =
%m
%n
</pre>
<p>This creates another appender named <code>SCREEN</code>. We then need to
tell
our
root logger to
use
this appender
as well:</p>
<pre class=
"prettyprint"
>log4perl.rootLogger = DEBUG, LOG1, SCREEN</pre>
<p>Now, restart your application, and visit a route that
has
logging installed, and you will see your
log
message not only goes to the <i>logs/mylog.
log
</i> file, but also displays on the console running your
application. Easy!</p>
<h2><a name=
"some_gotchas"
></a>Some Gotchas</h2>
<p>There are a couple of important nuances you should be aware of:</p>
<ul>
<li><a name=
"item_Environment_configuration_replaces_logging_configuration"
></a><b>Environment configuration replaces logging configuration</b>
<p>If you put your logging configuration in <i>config.yml</i> rather than one of your environment-specific
configuration files (such as <i>environments/development.yml</i>), you stand a good chance of not using
the logging configuration you think you are using. The
default
configuration file
for
the development
environment,
for
example, logs only to the console. If you put your Log4perl configuration in <i>config.yml</i>
and don't change your development configuration file, your Log4perl configuration will be passed over
for
the
default
console logger.</p>
<p>From
my
own experience, <b>always</b> configure your logger in your environment-specific configuration,
unless
</li>
<li><a name=
"item_Core_level_messages_are_passed_as_log_level_trace__but_will_not_be_passed_unless_Dancer2_s_log_level_is_core_"
></a><b>Core level messages are passed as
log
level trace, but will not be passed
unless
Dancer2's
log
level is core.</b>
<p>Since <code>core</code> doesn't have a good corresponding level in Log4perl, <code>core</code> level messages are sent
over to Log4perl at the <code>trace</code>
log
level. This <b>only</b> happens
when
you set Dancer2's
log
level in your
<i>config.yml</i> file to <code>core</code> however. So your preferred
log
level setting is respected, even
if
<code>core</code> level
messages have to be reported at a different level.</p>
</li>
<li><a name=
"item__code_log__code__should_be_set_a_lower_priority_than_the_lowest_priority_as_set_in_your_Log4perl_configuration_"
></a><b><code>
log
</code> should be set a lower priority than the lowest priority as set in your Log4perl configuration.<...
<p>If it isn't, the
log
messages will not be passed to Log4perl.</p>
</li>
</ul>
<h2><a name=
"conclusion"
></a>Conclusion</h2>
<p>If Log4perl is all the logging you need in your Dancer2 applications, then <code>Dancer2::Logger::Log4perl</code> is well worth
a look. It gives you much of the functionality available to Log4perl
while
using the logging syntax built into Dancer2.
This article should give you sufficient grounding to get going
with
Log4perl in your Dancer2 applications.</p>
<h2><a name=
"further_reading"
></a>Further Reading</h2>
<ul>
<li><a name=
"item__a_href__https___metacpan_org_pod_Dancer2__Logger__Log4perl__Dancer2__Logger__Log4perl__a_"
></a><b><a href=
"https://metacpan.org/pod/Dancer2::Logger::Log4perl"
>Dancer2::Logger::Log4perl</a></b>
</li>
<li><a name=
"item__a_href__https___metacpan_org_pod_Log__Log4perl__Log4perl__a_"
></a><b><a href=
"https://metacpan.org/pod/Log::Log4perl"
>Log4perl</a></b>
</li>
<li><a name=
"item__a_href__https___www_perl_com_pub_2002_09_11_log4perl_html___Log4Perl_Tutorial__a_"
></a><b><a href=
"https://www.perl.com/pub/2002/09/11/log4perl.html/"
>Log4Perl Tutorial</a></b>
</li>
</ul>
<h2><a name=
"author"
></a>Author</h2>
<p>This article
has
been written by Jason Crome (CromeDome)
for
the Perl Dancer
Advent Calendar 2018.</p>
<h2><a name=
"copyright"
></a>Copyright</h2>
<p>No copyright retained. Enjoy.</p>
<p>Jason A. Crome</p>
devdata/http_advent.perldancer.org_2018_20 view on Meta::CPAN
474849505152535455565758596061626364656667686970717273747576777879808182</li>
</ul>
</div>
<div id=
"content"
>
<div class=
"pod-document"
><h1><a name=
"testing_dancer_with_test__mojo"
></a>Testing Dancer
with
Test::Mojo</h1>
<p>Authors of Dancer (and other) PSGI applications are probably accustomed to <a href=
"https://metacpan.org/pod/distribution/Dancer2/lib/Dancer2/Manual.pod#TESTING"
>testing</a>
with
<a href=
"https://metacpan.org/pod/Plack::Test"
>Plack::Test</a>, and ...
<p>During advent
last
year, I wrote about <a href=
"https://mojolicious.org/perldoc/Test/Mojo"
>Test::Mojo</a>, showing the many easy and (dare I
say
) fun ways that you can
use
it to test your Mojolicious applications.
If you missed it, go <a href=
"https://mojolicious.io/blog/2017/12/09/day-9-the-best-way-to-test/"
>check it out</a>.</p>
<p>I expect there are at least a few of you out there who
read
that and think,
"I'd love to use that, but I don't use Mojolicious!"
; well, you're in luck!
With just a little role to bridge the gap, you can
use
Test::Mojo to test your PSGI applications too!</p>
<h2><a name=
"mounting_psgi_applications"
></a>Mounting PSGI Applications</h2>
<p>Mojolicious itself doesn
't use the <a href="https://metacpan.org/pod/PSGI">PSGI</a> protocol, owing to certain features that it doesn'
t provide and which are necessary
for
certain asynchronous operations.
That said, you can serve a Mojolicious application on a PSGI server by using <a href=
"https://mojolicious.org/perldoc/Mojo/Server/PSGI"
>Mojo::Server::PSGI</a>.
This Mojolicious-core module is automatically used
for
you
when
your Mojolicious-based app detects that it
has
started under a PSGI server (e.g. plackup or Starman).</p>
<p>While translating between a Mojo app and a PSGI server is core functionality, doing the opposite, translating between a PSGI app and a Mojolicious server (or app, as you'll see) is available as a third party module.
<a href=
"https://metacpan.org/pod/Mojolicious::Plugin::MountPSGI"
>Mojolicious::Plugin::MountPSGI</a>, as it's name implies, can mount a PSGI application into a Mojolicious-based one.
To
do
so, it builds a new, empty Mojolicious application that translates all requests to PSGI environments
before
dispatching to it as
with
any <a href=
"https://mojolicious.org/perldoc/Mojolicious/Plugin/Mount"
>mount</a>-ed application.</p>
<h2><a name=
"testing_using_test__mojo"
></a>Testing using Test::Mojo</h2>
<p>Once you can
do
that, it is trivial to take a PSGI application, wrap it
with
MountPSGI, and set it as the application
for
use
with
Test::Mojo.
Still, to make it even easier, that
has
all been done
for
you in <a href=
"https://metacpan.org/pod/Test::Mojo::Role::PSGI"
>Test::Mojo::Role::PSGI</a>.</p>
<p>Like any <a href=
"https://mojolicious.io/blog/2017/12/13/day-13-more-about-roles/"
>Mojolicious Role</a>, we can
use
<code>with_roles</code> to create a (mostly anonymous) subclass
with
the role applied.
my
$class
= Test::Mojo-
>
;with_roles(
'+PSGI'
);</pre>
<p>Then you instantiate that role
with
the path to the PSGI application, or
else
the PSGI application itself.</p>
<p>Since you're using roles, which are all about composition, you can also apply other roles that you might <a href=
"https://metacpan.org/search?q=%22Test%3A%3AMojo%3A%3ARole%22"
>find on CPAN</a>.</p>
<h2><a name=
"an_example"
></a>An Example</h2>
<p>As an example, let's
say
we have a simple application script (named <code>app.psgi</code>) that can render a <code>
"hello world"
</code> or <code>
"hello $user"
</code> in several formats.
I'll allow a plain text response, JSON, and templated HTML (using the <a href=
"https://metacpan.org/pod/Dancer2::Template::Simple"
>simple</a> template to keep this concise).</p>
devdata/http_advent.perldancer.org_2018_20 view on Meta::CPAN
106107108109110111112113114115116117118119120121122123124125<pre class=
"prettyprint"
>
<
;dl id=
"data"
>
;
<
;dt id=
"hello"
>
;hello
<
;/dt
>
;
<
;dd
>
;
<
;% name %
>
;
<
;/dd
>
;
<
;/dl
>
;</pre>
<p>The <a href=
"https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dl"
>dl</a>, <a href=
"https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dt"
>dt</a> and <a href=
"https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dd"
>dd</a> tags...
The HTML I
've built, while nice for display isn'
t necessarily nice
for
querying programmatically, this is on purpose
for
the example.</p>
<h2><a name=
"the_tests"
></a>The Tests</h2>
<p>Of course we could start the application
with
<a href=
"https://metacpan.org/pod/distribution/Plack/script/plackup"
>plackup</a> but that
's not what we'
re trying to
do
.
I'll break the test script down a bit but
if
you want to see any of these files look at the <a href=
"https://github.com/MojoliciousDotIO/mojolicious.io/tree/master/blog/2018/12/20/testing-dancer/ex"
>blog repo</a>
for
a full listing.
Instead, let's load this into a test script.</p>
use
warnings;
use
utf8;
use
IO::Handle;
devdata/http_advent.perldancer.org_2018_20 view on Meta::CPAN
176177178179180181182183184185186187188189190191192193194195196
-
>
;content_type_like(
qr[text/html]
)
-
>
;text_is(
'dl#data dt#hello + dd'
,
'world'
);
$t
-
>
;post_ok(
'/html'
=
>
; form =
>
; { name =
>
;
'grinch'
})
-
>
;status_is(200)
-
>
;content_type_like(
qr[text/html]
)
-
>
;text_is(
'dl#data dt#hello + dd'
,
'grinch'
);
done_testing;</pre>
<p>In this year
's Mojolicious advent calendar, we'
ve already seen <a href=
"https://mojolicious.io/blog/2018/12/05/compound-selectors/"
>some</a> <a href=
"https://mojolicious.io/blog/2018/12/14/a-practical-example-of-mojo-dom/"
>great</a> <a href="https...
The point remains however, testing HTML responses
with
CSS selectors allows you to make your tests targetd in a way that allows you to
write
more and better tests since you don't have to hack
around
extracting the bits you want.</p>
<h2><a name=
"testing_websockets"
></a>Testing WebSockets</h2>
<p>Ok so that
's great and all, but of course now it comes to the point you'
ve all been waiting
for
: can you test WebSockets?
As Jason Crome mentioned in his <a href=
"http://advent.perldancer.org/2018/13"
>Twelve Days of Dancer</a>
"State of Dancer"
, you can now dance
with
WebSockets via <a href=
"https://metacpan.org/pod/Dancer2::Plugin::WebSocket"
>Dancer2::Plugin::WebSocket...
<p>Well, so far not via the role I showed above.
It might be possible, but it would involve learning deep PSGI magick that I
'm not sure I'
m smart enough to
do
; patches welcome obviously :D.</p>
<p>Still I mentioned above that Test::Mojo can test anything it can access via an fully qualified URL, so let's just start up a server and test it!
I'll
use
the <a href=
"https://github.com/yanick/Dancer2-Plugin-WebSocket/tree/releases/example"
>example bundled
with
the plugin</a>
for
simplicty.</p>
devdata/http_advent.perldancer.org_2018_21 view on Meta::CPAN
158159160161162163164165166167168169170171172173174175176177<p>Screen readers know to ignore this because it
's hidden, and because it'
s hidden, the page won't show this field.
As a user, you won't ever have to fill it in.</p>
<p>Thankfully, a lot of spam bots are stupid, and blindly fill in any form field they see. They can't parse the CSS
in such a way that they can
tell
one of the fields is unusably hidden, and they often don't pay attention to
the hidden attribute... so they fall right into
our
trap. Enjoy your free education, spambot!</p>
<h2><a name=
"final_thoughts"
></a>Final thoughts</h2>
<p>Is it foolproof? No, not by a longshot. Anyone who really wants to is going to find a way to spam you anyhow,
given
a sufficient amount of
time
and motivation. But this will slow many down, and prevent a bunch, and
you won't drive sighted users away from your site. In the three and a half years I have used this, only two
spambots have made it through, and
my
server logs indicate that many others have tried.</p>
<h2><a name=
"author"
></a>Author</h2>
<p>This article
has
been written by Jason Crome (CromeDome)
for
the Perl Dancer
Advent Calendar 2018.</p>
<h2><a name=
"copyright"
></a>Copyright</h2>
<p>No copyright retained. Enjoy.</p>
<p>Jason A. Crome</p>
</div>
devdata/http_advent.perldancer.org_2018_23 view on Meta::CPAN
434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
<li><a class=
"feed"
href=
"/feed/2018"
>RSS</a></li>
</ul>
</li>
</ul>
</div>
<div id=
"content"
>
<div class=
"pod-document"
><h1><a name=
"dancer2__logger__console__colored"
></a>Dancer2::Logger::Console::Colored</h1>
<p>During development the console output of your app is often one of the most important tools.
But sometimes there is just too much noise, and changing the
log
settings all the
time
is not convenient.
Of course you can
grep
the
log
or scroll, but wouldn
't it be nice to get visual clues for what you'
re looking
for
?</p>
<p>With <a href=
"https://metacpan.org/module/Dancer2::Logger::Console::Colored"
>Dancer2::Logger::Console::Colored</a> you can
do
that. It's a drop-in replacement
for
the
default
console logger, but
with
color. It will make the message appear in color depending on the levels. You can turn
it on by setting your logger in your environment config file:</p>
<pre class=
"prettyprint"
>logger:
"Console::Colored"
</pre>
<p>Your
log
will instantly become cheerful and seasonal according to this
default
configuration:</p>
<pre class=
"prettyprint"
>
# config.yml (these are the defaults)
engines:
logger:
Console::Colored:
colored_origin:
"cyan"
colored_levels:
core:
"bold bright_white"
debug:
"bold bright_blue"
info:
"bold green"
warning:
"bold yellow"
error:
"bold yellow on_red"
colored_messages:
core:
"bold bright_white"
debug:
"bold bright_blue"
info:
"bold green"
warning:
"bold yellow"
error:
"bold yellow on_red"
</pre>
<img src=
"/images/2018/23/log-1.png"
>
<p>The <code>colored_origin</code> refers to the part of the message that shows the
package
that this
message originated in, as well as the file name and line.</p>
[main:28764] debug @2018-12-19 20:31:06
>
; Hello World in test.pl l. 6
^^^^ ^^^^^^^ ^</pre>
<p>The <code>colored_levels</code> are the
log
levels themselves.</p>
<pre class=
"prettyprint"
>[main:28764] debug @2018-12-19 20:31:06
>
; Hello World in test.pl l. 6
^^^^^</pre>
<p>And the <code>colored_messages</code> refer to the actual message.</p>
<pre class=
"prettyprint"
>[main:28764] debug @2018-12-19 20:31:06
>
; Hello World in test.pl l. 6
^^^^^^^^^^^</pre>
<p>The colors are the same kind of color strings used by <a href=
"https://metacpan.org/module/Term::ANSIColor"
>Term::ANSIColor</a>. The first
color refers to the foreground, and the second one to the background. You can add an optional <code>bold</code>.</p>
<pre class=
"prettyprint"
>[bold] [foreground] [on_background]</pre>
<h2><a name=
"changing_the_log_message"
></a>Changing the
log
message</h2>
<p>If you don't like the
default
log
format
, you can change it
with
the <code>log_format</code> setting, which is
documented in detail in <a href=
"https://metacpan.org/module/Dancer2::Core::Role::Logger"
>Dancer2::Core::Role::Logger</a>. You can just stick it in
with
the other configuration.</p>
<pre class=
"prettyprint"
>
# config.yml (this is the default)
engines:
logger:
Console::Colored:
log_format:
"[%a:%P] %L @%T> %m in %f l. %l"
</pre>
<p>Usually you would run a single-worker server during development, so there is
no
need
for
request IDs.
And since the message is now colored, we can also drop the
log
level. How about a simple
format
like
the following:</p>
<pre class=
"prettyprint"
>
%t
%f
:
%l
>
;
%m
# 20/Dec/2018 16:41:07 MyApp.pm:22> Hello World</pre>
<h2><a name=
"i_know_regular_expressions_"
></a>I know regular expressions!</h2>
<p>Sometimes just reading the
log
is not enough. You're looking
for
a specific piece of information. There
are three pages of
log
for
a single request, and debugging is hard work. Grepping is an option,
but you need to look
for
a complex pattern. Maybe a product SKU, a date or some other piece of information.</p>
<p>With Dancer2::Logger::Console::Colored you can actually make it come out
with
a background color so you
can spot it easily, without having to change your code (or your added debugging
log
statements). Instead,
you can put a regular expression into your configuration file.</p>
<pre class=
"prettyprint"
>
# config.yml
engines:
logger:
Console::Colored:
colored_regex:
- re:
"customer number \d+"
color:
"bold red"
</pre>
<p>This will find customer numbers according to the pattern and make them bold and red. You can supply
a regular expression pattern and a color setting.</p>
<img src=
"/images/2018/23/log-2.png"
>
<p>Note that there is a list under the <code>colored_regex</code> key. This means you can actually have
multiple patterns. Even really simple ones that make <code>
</code> style debugging easy
and attractive.</p>
<pre class=
"prettyprint"
>
# config.yml
engines:
logger:
Console::Colored:
colored_regex:
- re:
"customer number \d+"
color:
"bold red"
- re:
"\bhere\b"
color:
"white on_green"
</pre>
<p>If you want to highlight an entire
log
message in a specific color, you can set a pattern that captures
everything. However, this works on the message only, and not on the entire formatted line of
log
output.
(Patches welcome!)</p>
<pre class=
"prettyprint"
>
# config.yml
engines:
logger:
Console::Colored:
colored_regex:
- re:
".+here.+"
color:
"white on_red"
</pre>
<img src=
"/images/2018/23/log-3.png"
>
<p>This opens up a lot of possibilities,
for
debugging as well as
for
general logging during development.</p>
<h2><a name=
"what_else"
></a>What
else
?</h2>
<p>There are still some feature ideas in the backlog, but it's already pretty useful. If you would
like to see additional features, please feel free to
<a href=
"https://metacpan.org/module/Mojo::Log::Colored"
>Mojo::Log::Colored</a>, which is in the early stages of development, but works quite well.</p>
<h2><a name=
"author"
></a>Author</h2>
<p>This article
has
been written by Julien Fiegehenn (simbabque)
for
the Perl Dancer
Advent Calendar 2018.</p>
<h2><a name=
"copyright"
></a>Copyright</h2>
devdata/http_advent.perldancer.org_2018_24 view on Meta::CPAN
69707172737475767778798081828384858687888990</li>
<li><a name=
"item_New_Website"
></a><b>New Website</b>
<p>There
's a lot of great information on the Dancer website, but it'
s looking tired. There's a
lot of new information to be added, and
with
a fresh coat of paint, Dancer's website will be as
great as the software powering it.</p>
</li>
<li><a name=
"item_More_configuration_options"
></a><b>More configuration options</b>
<p>Configuration in Dancer2 leaves a lot to be desired (and I'll admit that I am a big part of
the reason
for
why that is). Look
for
an overhaul of the configuration
system
in 2019, which
starts
with
a barebones configuration
system
and allows
for
pluggable configuration engines
(much like loggers and sessions in Dancer2). Since we are dedicated to not breaking working
code, all of the existing configuration logic would be packaged as a plugin and enabled
out of the box.</p>
</li>
<li><a name=
"item_Inline_parameter_type_checking"
></a><b>Inline parameter type checking</b>
<p>Very similar in many ways to Sawyer's <a href=
"https://metacpan.org/pod/Dancer2::Plugin::ParamTypes"
>Dancer2::Plugin::ParamTypes</a>
plugin, but provided as core functionality. SysPete is actively developing a way to provide
(optional) type checking of parameters within the core of Dancer2.</p>
about type checking parameters passed to your Dancer applications, I highly recommend
it. It will change the way you
do
data validation.</p>
</li>
devscripts/update view on Meta::CPAN
424344454647484950515253
ignore_empty
=> 1,
namespace
=>
'Acme::CPANModules::Import::PerlDancerAdvent'
,
user_agent
=>
'Mozilla/5.0'
,
dist_dir
=>
"$Bin/.."
,
},
'app'
,
);
Perinci::CmdLine::Any->new(
url
=>
'/main/app'
,
log
=> 1,
)->run;