-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlogging-cookbook.html
More file actions
2584 lines (2420 loc) · 294 KB
/
logging-cookbook.html
File metadata and controls
2584 lines (2420 loc) · 294 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="zh_TW">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Logging Cookbook — Python 3.7.0 說明文件</title>
<link rel="stylesheet" href="../_static/pydoctheme.css" type="text/css" />
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
<script type="text/javascript" id="documentation_options" data-url_root="../" src="../_static/documentation_options.js"></script>
<script type="text/javascript" src="../_static/jquery.js"></script>
<script type="text/javascript" src="../_static/underscore.js"></script>
<script type="text/javascript" src="../_static/doctools.js"></script>
<script type="text/javascript" src="../_static/translations.js"></script>
<script type="text/javascript" src="../_static/sidebar.js"></script>
<link rel="search" type="application/opensearchdescription+xml"
title="在 Python 3.7.0 說明文件 中搜尋"
href="../_static/opensearch.xml"/>
<link rel="author" title="關於這些文件" href="../about.html" />
<link rel="index" title="索引" href="../genindex.html" />
<link rel="search" title="搜尋" href="../search.html" />
<link rel="copyright" title="Copyright" href="../copyright.html" />
<link rel="next" title="如何使用正規表達式" href="regex.html" />
<link rel="prev" title="如何使用 Logging 模組" href="logging.html" />
<link rel="shortcut icon" type="image/png" href="../_static/py.png" />
<link rel="canonical" href="https://docs.python.org/3/howto/logging-cookbook.html" />
<script type="text/javascript" src="../_static/copybutton.js"></script>
<script type="text/javascript" src="../_static/switchers.js"></script>
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>瀏覽</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../genindex.html" title="General Index"
accesskey="I">索引</a></li>
<li class="right" >
<a href="../py-modindex.html" title="Python 模組索引"
>模組</a> |</li>
<li class="right" >
<a href="regex.html" title="如何使用正規表達式"
accesskey="N">下一頁</a> |</li>
<li class="right" >
<a href="logging.html" title="如何使用 Logging 模組"
accesskey="P">上一頁</a> |</li>
<li><img src="../_static/py.png" alt=""
style="vertical-align: middle; margin-top: -1px"/></li>
<li><a href="https://www.python.org/">Python</a> »</li>
<li>
<span class="language_switcher_placeholder">zh_TW</span>
<span class="version_switcher_placeholder">3.7.0</span>
<a href="../index.html">Documentation </a> »
</li>
<li class="nav-item nav-item-1"><a href="index.html" accesskey="U">Python HOWTOs</a> »</li>
<li class="right">
<div class="inline-search" style="display: none" role="search">
<form class="inline-search" action="../search.html" method="get">
<input placeholder="Quick search" type="text" name="q" />
<input type="submit" value="Go" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
<script type="text/javascript">$('.inline-search').show(0);</script>
|
</li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<div class="section" id="logging-cookbook">
<span id="id1"></span><h1>Logging Cookbook<a class="headerlink" href="#logging-cookbook" title="本標題的永久連結">¶</a></h1>
<table class="docutils field-list" frame="void" rules="none">
<col class="field-name" />
<col class="field-body" />
<tbody valign="top">
<tr class="field-odd field"><th class="field-name">Author:</th><td class="field-body">原文作者 Vinay Sajip <vinay_sajip at red-dove dot com></td>
</tr>
</tbody>
</table>
<p>This page contains a number of recipes related to logging, which have been found
useful in the past.</p>
<div class="section" id="using-logging-in-multiple-modules">
<h2>Using logging in multiple modules<a class="headerlink" href="#using-logging-in-multiple-modules" title="本標題的永久連結">¶</a></h2>
<p>Multiple calls to <code class="docutils literal notranslate"><span class="pre">logging.getLogger('someLogger')</span></code> return a reference to the
same logger object. This is true not only within the same module, but also
across modules as long as it is in the same Python interpreter process. It is
true for references to the same object; additionally, application code can
define and configure a parent logger in one module and create (but not
configure) a child logger in a separate module, and all logger calls to the
child will pass up to the parent. Here is a main module:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">logging</span>
<span class="kn">import</span> <span class="nn">auxiliary_module</span>
<span class="c1"># create logger with 'spam_application'</span>
<span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="s1">'spam_application'</span><span class="p">)</span>
<span class="n">logger</span><span class="o">.</span><span class="n">setLevel</span><span class="p">(</span><span class="n">logging</span><span class="o">.</span><span class="n">DEBUG</span><span class="p">)</span>
<span class="c1"># create file handler which logs even debug messages</span>
<span class="n">fh</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">FileHandler</span><span class="p">(</span><span class="s1">'spam.log'</span><span class="p">)</span>
<span class="n">fh</span><span class="o">.</span><span class="n">setLevel</span><span class="p">(</span><span class="n">logging</span><span class="o">.</span><span class="n">DEBUG</span><span class="p">)</span>
<span class="c1"># create console handler with a higher log level</span>
<span class="n">ch</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">StreamHandler</span><span class="p">()</span>
<span class="n">ch</span><span class="o">.</span><span class="n">setLevel</span><span class="p">(</span><span class="n">logging</span><span class="o">.</span><span class="n">ERROR</span><span class="p">)</span>
<span class="c1"># create formatter and add it to the handlers</span>
<span class="n">formatter</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">Formatter</span><span class="p">(</span><span class="s1">'</span><span class="si">%(asctime)s</span><span class="s1"> - </span><span class="si">%(name)s</span><span class="s1"> - </span><span class="si">%(levelname)s</span><span class="s1"> - </span><span class="si">%(message)s</span><span class="s1">'</span><span class="p">)</span>
<span class="n">fh</span><span class="o">.</span><span class="n">setFormatter</span><span class="p">(</span><span class="n">formatter</span><span class="p">)</span>
<span class="n">ch</span><span class="o">.</span><span class="n">setFormatter</span><span class="p">(</span><span class="n">formatter</span><span class="p">)</span>
<span class="c1"># add the handlers to the logger</span>
<span class="n">logger</span><span class="o">.</span><span class="n">addHandler</span><span class="p">(</span><span class="n">fh</span><span class="p">)</span>
<span class="n">logger</span><span class="o">.</span><span class="n">addHandler</span><span class="p">(</span><span class="n">ch</span><span class="p">)</span>
<span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">'creating an instance of auxiliary_module.Auxiliary'</span><span class="p">)</span>
<span class="n">a</span> <span class="o">=</span> <span class="n">auxiliary_module</span><span class="o">.</span><span class="n">Auxiliary</span><span class="p">()</span>
<span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">'created an instance of auxiliary_module.Auxiliary'</span><span class="p">)</span>
<span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">'calling auxiliary_module.Auxiliary.do_something'</span><span class="p">)</span>
<span class="n">a</span><span class="o">.</span><span class="n">do_something</span><span class="p">()</span>
<span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">'finished auxiliary_module.Auxiliary.do_something'</span><span class="p">)</span>
<span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">'calling auxiliary_module.some_function()'</span><span class="p">)</span>
<span class="n">auxiliary_module</span><span class="o">.</span><span class="n">some_function</span><span class="p">()</span>
<span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">'done with auxiliary_module.some_function()'</span><span class="p">)</span>
</pre></div>
</div>
<p>Here is the auxiliary module:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">logging</span>
<span class="c1"># create logger</span>
<span class="n">module_logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="s1">'spam_application.auxiliary'</span><span class="p">)</span>
<span class="k">class</span> <span class="nc">Auxiliary</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="s1">'spam_application.auxiliary.Auxiliary'</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">'creating an instance of Auxiliary'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">do_something</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">'doing something'</span><span class="p">)</span>
<span class="n">a</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">+</span> <span class="mi">1</span>
<span class="bp">self</span><span class="o">.</span><span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">'done doing something'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">some_function</span><span class="p">():</span>
<span class="n">module_logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">'received a call to "some_function"'</span><span class="p">)</span>
</pre></div>
</div>
<p>The output looks like this:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>2005-03-23 23:47:11,663 - spam_application - INFO -
creating an instance of auxiliary_module.Auxiliary
2005-03-23 23:47:11,665 - spam_application.auxiliary.Auxiliary - INFO -
creating an instance of Auxiliary
2005-03-23 23:47:11,665 - spam_application - INFO -
created an instance of auxiliary_module.Auxiliary
2005-03-23 23:47:11,668 - spam_application - INFO -
calling auxiliary_module.Auxiliary.do_something
2005-03-23 23:47:11,668 - spam_application.auxiliary.Auxiliary - INFO -
doing something
2005-03-23 23:47:11,669 - spam_application.auxiliary.Auxiliary - INFO -
done doing something
2005-03-23 23:47:11,670 - spam_application - INFO -
finished auxiliary_module.Auxiliary.do_something
2005-03-23 23:47:11,671 - spam_application - INFO -
calling auxiliary_module.some_function()
2005-03-23 23:47:11,672 - spam_application.auxiliary - INFO -
received a call to 'some_function'
2005-03-23 23:47:11,673 - spam_application - INFO -
done with auxiliary_module.some_function()
</pre></div>
</div>
</div>
<div class="section" id="logging-from-multiple-threads">
<h2>Logging from multiple threads<a class="headerlink" href="#logging-from-multiple-threads" title="本標題的永久連結">¶</a></h2>
<p>Logging from multiple threads requires no special effort. The following example
shows logging from the main (initial) thread and another thread:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">logging</span>
<span class="kn">import</span> <span class="nn">threading</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="k">def</span> <span class="nf">worker</span><span class="p">(</span><span class="n">arg</span><span class="p">):</span>
<span class="k">while</span> <span class="ow">not</span> <span class="n">arg</span><span class="p">[</span><span class="s1">'stop'</span><span class="p">]:</span>
<span class="n">logging</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s1">'Hi from myfunc'</span><span class="p">)</span>
<span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.5</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
<span class="n">logging</span><span class="o">.</span><span class="n">basicConfig</span><span class="p">(</span><span class="n">level</span><span class="o">=</span><span class="n">logging</span><span class="o">.</span><span class="n">DEBUG</span><span class="p">,</span> <span class="nb">format</span><span class="o">=</span><span class="s1">'</span><span class="si">%(relativeCreated)6d</span><span class="s1"> </span><span class="si">%(threadName)s</span><span class="s1"> </span><span class="si">%(message)s</span><span class="s1">'</span><span class="p">)</span>
<span class="n">info</span> <span class="o">=</span> <span class="p">{</span><span class="s1">'stop'</span><span class="p">:</span> <span class="kc">False</span><span class="p">}</span>
<span class="n">thread</span> <span class="o">=</span> <span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">worker</span><span class="p">,</span> <span class="n">args</span><span class="o">=</span><span class="p">(</span><span class="n">info</span><span class="p">,))</span>
<span class="n">thread</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
<span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">logging</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s1">'Hello from main'</span><span class="p">)</span>
<span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.75</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">KeyboardInterrupt</span><span class="p">:</span>
<span class="n">info</span><span class="p">[</span><span class="s1">'stop'</span><span class="p">]</span> <span class="o">=</span> <span class="kc">True</span>
<span class="k">break</span>
<span class="n">thread</span><span class="o">.</span><span class="n">join</span><span class="p">()</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">'__main__'</span><span class="p">:</span>
<span class="n">main</span><span class="p">()</span>
</pre></div>
</div>
<p>When run, the script should print something like the following:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span> 0 Thread-1 Hi from myfunc
3 MainThread Hello from main
505 Thread-1 Hi from myfunc
755 MainThread Hello from main
1007 Thread-1 Hi from myfunc
1507 MainThread Hello from main
1508 Thread-1 Hi from myfunc
2010 Thread-1 Hi from myfunc
2258 MainThread Hello from main
2512 Thread-1 Hi from myfunc
3009 MainThread Hello from main
3013 Thread-1 Hi from myfunc
3515 Thread-1 Hi from myfunc
3761 MainThread Hello from main
4017 Thread-1 Hi from myfunc
4513 MainThread Hello from main
4518 Thread-1 Hi from myfunc
</pre></div>
</div>
<p>This shows the logging output interspersed as one might expect. This approach
works for more threads than shown here, of course.</p>
</div>
<div class="section" id="multiple-handlers-and-formatters">
<h2>Multiple handlers and formatters<a class="headerlink" href="#multiple-handlers-and-formatters" title="本標題的永久連結">¶</a></h2>
<p>Loggers are plain Python objects. The <a class="reference internal" href="../library/logging.html#logging.Logger.addHandler" title="logging.Logger.addHandler"><code class="xref py py-meth docutils literal notranslate"><span class="pre">addHandler()</span></code></a> method has no
minimum or maximum quota for the number of handlers you may add. Sometimes it
will be beneficial for an application to log all messages of all severities to a
text file while simultaneously logging errors or above to the console. To set
this up, simply configure the appropriate handlers. The logging calls in the
application code will remain unchanged. Here is a slight modification to the
previous simple module-based configuration example:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">logging</span>
<span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="s1">'simple_example'</span><span class="p">)</span>
<span class="n">logger</span><span class="o">.</span><span class="n">setLevel</span><span class="p">(</span><span class="n">logging</span><span class="o">.</span><span class="n">DEBUG</span><span class="p">)</span>
<span class="c1"># create file handler which logs even debug messages</span>
<span class="n">fh</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">FileHandler</span><span class="p">(</span><span class="s1">'spam.log'</span><span class="p">)</span>
<span class="n">fh</span><span class="o">.</span><span class="n">setLevel</span><span class="p">(</span><span class="n">logging</span><span class="o">.</span><span class="n">DEBUG</span><span class="p">)</span>
<span class="c1"># create console handler with a higher log level</span>
<span class="n">ch</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">StreamHandler</span><span class="p">()</span>
<span class="n">ch</span><span class="o">.</span><span class="n">setLevel</span><span class="p">(</span><span class="n">logging</span><span class="o">.</span><span class="n">ERROR</span><span class="p">)</span>
<span class="c1"># create formatter and add it to the handlers</span>
<span class="n">formatter</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">Formatter</span><span class="p">(</span><span class="s1">'</span><span class="si">%(asctime)s</span><span class="s1"> - </span><span class="si">%(name)s</span><span class="s1"> - </span><span class="si">%(levelname)s</span><span class="s1"> - </span><span class="si">%(message)s</span><span class="s1">'</span><span class="p">)</span>
<span class="n">ch</span><span class="o">.</span><span class="n">setFormatter</span><span class="p">(</span><span class="n">formatter</span><span class="p">)</span>
<span class="n">fh</span><span class="o">.</span><span class="n">setFormatter</span><span class="p">(</span><span class="n">formatter</span><span class="p">)</span>
<span class="c1"># add the handlers to logger</span>
<span class="n">logger</span><span class="o">.</span><span class="n">addHandler</span><span class="p">(</span><span class="n">ch</span><span class="p">)</span>
<span class="n">logger</span><span class="o">.</span><span class="n">addHandler</span><span class="p">(</span><span class="n">fh</span><span class="p">)</span>
<span class="c1"># 'application' code</span>
<span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s1">'debug message'</span><span class="p">)</span>
<span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">'info message'</span><span class="p">)</span>
<span class="n">logger</span><span class="o">.</span><span class="n">warn</span><span class="p">(</span><span class="s1">'warn message'</span><span class="p">)</span>
<span class="n">logger</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="s1">'error message'</span><span class="p">)</span>
<span class="n">logger</span><span class="o">.</span><span class="n">critical</span><span class="p">(</span><span class="s1">'critical message'</span><span class="p">)</span>
</pre></div>
</div>
<p>Notice that the 『application』 code does not care about multiple handlers. All
that changed was the addition and configuration of a new handler named <em>fh</em>.</p>
<p>The ability to create new handlers with higher- or lower-severity filters can be
very helpful when writing and testing an application. Instead of using many
<code class="docutils literal notranslate"><span class="pre">print</span></code> statements for debugging, use <code class="docutils literal notranslate"><span class="pre">logger.debug</span></code>: Unlike the print
statements, which you will have to delete or comment out later, the logger.debug
statements can remain intact in the source code and remain dormant until you
need them again. At that time, the only change that needs to happen is to
modify the severity level of the logger and/or handler to debug.</p>
</div>
<div class="section" id="logging-to-multiple-destinations">
<span id="multiple-destinations"></span><h2>Logging to multiple destinations<a class="headerlink" href="#logging-to-multiple-destinations" title="本標題的永久連結">¶</a></h2>
<p>Let’s say you want to log to console and file with different message formats and
in differing circumstances. Say you want to log messages with levels of DEBUG
and higher to file, and those messages at level INFO and higher to the console.
Let’s also assume that the file should contain timestamps, but the console
messages should not. Here’s how you can achieve this:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">logging</span>
<span class="c1"># set up logging to file - see previous section for more details</span>
<span class="n">logging</span><span class="o">.</span><span class="n">basicConfig</span><span class="p">(</span><span class="n">level</span><span class="o">=</span><span class="n">logging</span><span class="o">.</span><span class="n">DEBUG</span><span class="p">,</span>
<span class="nb">format</span><span class="o">=</span><span class="s1">'</span><span class="si">%(asctime)s</span><span class="s1"> </span><span class="si">%(name)-12s</span><span class="s1"> </span><span class="si">%(levelname)-8s</span><span class="s1"> </span><span class="si">%(message)s</span><span class="s1">'</span><span class="p">,</span>
<span class="n">datefmt</span><span class="o">=</span><span class="s1">'%m-</span><span class="si">%d</span><span class="s1"> %H:%M'</span><span class="p">,</span>
<span class="n">filename</span><span class="o">=</span><span class="s1">'/temp/myapp.log'</span><span class="p">,</span>
<span class="n">filemode</span><span class="o">=</span><span class="s1">'w'</span><span class="p">)</span>
<span class="c1"># define a Handler which writes INFO messages or higher to the sys.stderr</span>
<span class="n">console</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">StreamHandler</span><span class="p">()</span>
<span class="n">console</span><span class="o">.</span><span class="n">setLevel</span><span class="p">(</span><span class="n">logging</span><span class="o">.</span><span class="n">INFO</span><span class="p">)</span>
<span class="c1"># set a format which is simpler for console use</span>
<span class="n">formatter</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">Formatter</span><span class="p">(</span><span class="s1">'</span><span class="si">%(name)-12s</span><span class="s1">: </span><span class="si">%(levelname)-8s</span><span class="s1"> </span><span class="si">%(message)s</span><span class="s1">'</span><span class="p">)</span>
<span class="c1"># tell the handler to use this format</span>
<span class="n">console</span><span class="o">.</span><span class="n">setFormatter</span><span class="p">(</span><span class="n">formatter</span><span class="p">)</span>
<span class="c1"># add the handler to the root logger</span>
<span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="s1">''</span><span class="p">)</span><span class="o">.</span><span class="n">addHandler</span><span class="p">(</span><span class="n">console</span><span class="p">)</span>
<span class="c1"># Now, we can log to the root logger, or any other logger. First the root...</span>
<span class="n">logging</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">'Jackdaws love my big sphinx of quartz.'</span><span class="p">)</span>
<span class="c1"># Now, define a couple of other loggers which might represent areas in your</span>
<span class="c1"># application:</span>
<span class="n">logger1</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="s1">'myapp.area1'</span><span class="p">)</span>
<span class="n">logger2</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="s1">'myapp.area2'</span><span class="p">)</span>
<span class="n">logger1</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s1">'Quick zephyrs blow, vexing daft Jim.'</span><span class="p">)</span>
<span class="n">logger1</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">'How quickly daft jumping zebras vex.'</span><span class="p">)</span>
<span class="n">logger2</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="s1">'Jail zesty vixen who grabbed pay from quack.'</span><span class="p">)</span>
<span class="n">logger2</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="s1">'The five boxing wizards jump quickly.'</span><span class="p">)</span>
</pre></div>
</div>
<p>When you run this, on the console you will see</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>root : INFO Jackdaws love my big sphinx of quartz.
myapp.area1 : INFO How quickly daft jumping zebras vex.
myapp.area2 : WARNING Jail zesty vixen who grabbed pay from quack.
myapp.area2 : ERROR The five boxing wizards jump quickly.
</pre></div>
</div>
<p>and in the file you will see something like</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>10-22 22:19 root INFO Jackdaws love my big sphinx of quartz.
10-22 22:19 myapp.area1 DEBUG Quick zephyrs blow, vexing daft Jim.
10-22 22:19 myapp.area1 INFO How quickly daft jumping zebras vex.
10-22 22:19 myapp.area2 WARNING Jail zesty vixen who grabbed pay from quack.
10-22 22:19 myapp.area2 ERROR The five boxing wizards jump quickly.
</pre></div>
</div>
<p>As you can see, the DEBUG message only shows up in the file. The other messages
are sent to both destinations.</p>
<p>This example uses console and file handlers, but you can use any number and
combination of handlers you choose.</p>
</div>
<div class="section" id="configuration-server-example">
<h2>Configuration server example<a class="headerlink" href="#configuration-server-example" title="本標題的永久連結">¶</a></h2>
<p>Here is an example of a module using the logging configuration server:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">logging</span>
<span class="kn">import</span> <span class="nn">logging.config</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="c1"># read initial config file</span>
<span class="n">logging</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">fileConfig</span><span class="p">(</span><span class="s1">'logging.conf'</span><span class="p">)</span>
<span class="c1"># create and start listener on port 9999</span>
<span class="n">t</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">listen</span><span class="p">(</span><span class="mi">9999</span><span class="p">)</span>
<span class="n">t</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
<span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="s1">'simpleExample'</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
<span class="c1"># loop through logging calls to see the difference</span>
<span class="c1"># new configurations make, until Ctrl+C is pressed</span>
<span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s1">'debug message'</span><span class="p">)</span>
<span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">'info message'</span><span class="p">)</span>
<span class="n">logger</span><span class="o">.</span><span class="n">warn</span><span class="p">(</span><span class="s1">'warn message'</span><span class="p">)</span>
<span class="n">logger</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="s1">'error message'</span><span class="p">)</span>
<span class="n">logger</span><span class="o">.</span><span class="n">critical</span><span class="p">(</span><span class="s1">'critical message'</span><span class="p">)</span>
<span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">KeyboardInterrupt</span><span class="p">:</span>
<span class="c1"># cleanup</span>
<span class="n">logging</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">stopListening</span><span class="p">()</span>
<span class="n">t</span><span class="o">.</span><span class="n">join</span><span class="p">()</span>
</pre></div>
</div>
<p>And here is a script that takes a filename and sends that file to the server,
properly preceded with the binary-encoded length, as the new logging
configuration:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="ch">#!/usr/bin/env python</span>
<span class="kn">import</span> <span class="nn">socket</span><span class="o">,</span> <span class="nn">sys</span><span class="o">,</span> <span class="nn">struct</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="s1">'rb'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
<span class="n">data_to_send</span> <span class="o">=</span> <span class="n">f</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
<span class="n">HOST</span> <span class="o">=</span> <span class="s1">'localhost'</span>
<span class="n">PORT</span> <span class="o">=</span> <span class="mi">9999</span>
<span class="n">s</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'connecting...'</span><span class="p">)</span>
<span class="n">s</span><span class="o">.</span><span class="n">connect</span><span class="p">((</span><span class="n">HOST</span><span class="p">,</span> <span class="n">PORT</span><span class="p">))</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'sending config...'</span><span class="p">)</span>
<span class="n">s</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="n">struct</span><span class="o">.</span><span class="n">pack</span><span class="p">(</span><span class="s1">'>L'</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">data_to_send</span><span class="p">)))</span>
<span class="n">s</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="n">data_to_send</span><span class="p">)</span>
<span class="n">s</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'complete'</span><span class="p">)</span>
</pre></div>
</div>
</div>
<div class="section" id="dealing-with-handlers-that-block">
<h2>Dealing with handlers that block<a class="headerlink" href="#dealing-with-handlers-that-block" title="本標題的永久連結">¶</a></h2>
<p>Sometimes you have to get your logging handlers to do their work without
blocking the thread you’re logging from. This is common in Web applications,
though of course it also occurs in other scenarios.</p>
<p>A common culprit which demonstrates sluggish behaviour is the
<a class="reference internal" href="../library/logging.handlers.html#logging.handlers.SMTPHandler" title="logging.handlers.SMTPHandler"><code class="xref py py-class docutils literal notranslate"><span class="pre">SMTPHandler</span></code></a>: sending emails can take a long time, for a
number of reasons outside the developer’s control (for example, a poorly
performing mail or network infrastructure). But almost any network-based
handler can block: Even a <a class="reference internal" href="../library/logging.handlers.html#logging.handlers.SocketHandler" title="logging.handlers.SocketHandler"><code class="xref py py-class docutils literal notranslate"><span class="pre">SocketHandler</span></code></a> operation may do a
DNS query under the hood which is too slow (and this query can be deep in the
socket library code, below the Python layer, and outside your control).</p>
<p>One solution is to use a two-part approach. For the first part, attach only a
<a class="reference internal" href="../library/logging.handlers.html#logging.handlers.QueueHandler" title="logging.handlers.QueueHandler"><code class="xref py py-class docutils literal notranslate"><span class="pre">QueueHandler</span></code></a> to those loggers which are accessed from
performance-critical threads. They simply write to their queue, which can be
sized to a large enough capacity or initialized with no upper bound to their
size. The write to the queue will typically be accepted quickly, though you
will probably need to catch the <a class="reference internal" href="../library/queue.html#queue.Full" title="queue.Full"><code class="xref py py-exc docutils literal notranslate"><span class="pre">queue.Full</span></code></a> exception as a precaution
in your code. If you are a library developer who has performance-critical
threads in their code, be sure to document this (together with a suggestion to
attach only <code class="docutils literal notranslate"><span class="pre">QueueHandlers</span></code> to your loggers) for the benefit of other
developers who will use your code.</p>
<p>The second part of the solution is <a class="reference internal" href="../library/logging.handlers.html#logging.handlers.QueueListener" title="logging.handlers.QueueListener"><code class="xref py py-class docutils literal notranslate"><span class="pre">QueueListener</span></code></a>, which has been
designed as the counterpart to <a class="reference internal" href="../library/logging.handlers.html#logging.handlers.QueueHandler" title="logging.handlers.QueueHandler"><code class="xref py py-class docutils literal notranslate"><span class="pre">QueueHandler</span></code></a>. A
<a class="reference internal" href="../library/logging.handlers.html#logging.handlers.QueueListener" title="logging.handlers.QueueListener"><code class="xref py py-class docutils literal notranslate"><span class="pre">QueueListener</span></code></a> is very simple: it’s passed a queue and some handlers,
and it fires up an internal thread which listens to its queue for LogRecords
sent from <code class="docutils literal notranslate"><span class="pre">QueueHandlers</span></code> (or any other source of <code class="docutils literal notranslate"><span class="pre">LogRecords</span></code>, for that
matter). The <code class="docutils literal notranslate"><span class="pre">LogRecords</span></code> are removed from the queue and passed to the
handlers for processing.</p>
<p>The advantage of having a separate <a class="reference internal" href="../library/logging.handlers.html#logging.handlers.QueueListener" title="logging.handlers.QueueListener"><code class="xref py py-class docutils literal notranslate"><span class="pre">QueueListener</span></code></a> class is that you
can use the same instance to service multiple <code class="docutils literal notranslate"><span class="pre">QueueHandlers</span></code>. This is more
resource-friendly than, say, having threaded versions of the existing handler
classes, which would eat up one thread per handler for no particular benefit.</p>
<p>An example of using these two classes follows (imports omitted):</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">que</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">Queue</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="c1"># no limit on size</span>
<span class="n">queue_handler</span> <span class="o">=</span> <span class="n">QueueHandler</span><span class="p">(</span><span class="n">que</span><span class="p">)</span>
<span class="n">handler</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">StreamHandler</span><span class="p">()</span>
<span class="n">listener</span> <span class="o">=</span> <span class="n">QueueListener</span><span class="p">(</span><span class="n">que</span><span class="p">,</span> <span class="n">handler</span><span class="p">)</span>
<span class="n">root</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">()</span>
<span class="n">root</span><span class="o">.</span><span class="n">addHandler</span><span class="p">(</span><span class="n">queue_handler</span><span class="p">)</span>
<span class="n">formatter</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">Formatter</span><span class="p">(</span><span class="s1">'</span><span class="si">%(threadName)s</span><span class="s1">: </span><span class="si">%(message)s</span><span class="s1">'</span><span class="p">)</span>
<span class="n">handler</span><span class="o">.</span><span class="n">setFormatter</span><span class="p">(</span><span class="n">formatter</span><span class="p">)</span>
<span class="n">listener</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
<span class="c1"># The log output will display the thread which generated</span>
<span class="c1"># the event (the main thread) rather than the internal</span>
<span class="c1"># thread which monitors the internal queue. This is what</span>
<span class="c1"># you want to happen.</span>
<span class="n">root</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="s1">'Look out!'</span><span class="p">)</span>
<span class="n">listener</span><span class="o">.</span><span class="n">stop</span><span class="p">()</span>
</pre></div>
</div>
<p>which, when run, will produce:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>MainThread: Look out!
</pre></div>
</div>
<div class="versionchanged">
<p><span class="versionmodified">3.5 版更變: </span>Prior to Python 3.5, the <a class="reference internal" href="../library/logging.handlers.html#logging.handlers.QueueListener" title="logging.handlers.QueueListener"><code class="xref py py-class docutils literal notranslate"><span class="pre">QueueListener</span></code></a> always passed every message
received from the queue to every handler it was initialized with. (This was
because it was assumed that level filtering was all done on the other side,
where the queue is filled.) From 3.5 onwards, this behaviour can be changed
by passing a keyword argument <code class="docutils literal notranslate"><span class="pre">respect_handler_level=True</span></code> to the
listener’s constructor. When this is done, the listener compares the level
of each message with the handler’s level, and only passes a message to a
handler if it’s appropriate to do so.</p>
</div>
</div>
<div class="section" id="sending-and-receiving-logging-events-across-a-network">
<span id="network-logging"></span><h2>Sending and receiving logging events across a network<a class="headerlink" href="#sending-and-receiving-logging-events-across-a-network" title="本標題的永久連結">¶</a></h2>
<p>Let’s say you want to send logging events across a network, and handle them at
the receiving end. A simple way of doing this is attaching a
<a class="reference internal" href="../library/logging.handlers.html#logging.handlers.SocketHandler" title="logging.handlers.SocketHandler"><code class="xref py py-class docutils literal notranslate"><span class="pre">SocketHandler</span></code></a> instance to the root logger at the sending end:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">logging</span><span class="o">,</span> <span class="nn">logging.handlers</span>
<span class="n">rootLogger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="s1">''</span><span class="p">)</span>
<span class="n">rootLogger</span><span class="o">.</span><span class="n">setLevel</span><span class="p">(</span><span class="n">logging</span><span class="o">.</span><span class="n">DEBUG</span><span class="p">)</span>
<span class="n">socketHandler</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">handlers</span><span class="o">.</span><span class="n">SocketHandler</span><span class="p">(</span><span class="s1">'localhost'</span><span class="p">,</span>
<span class="n">logging</span><span class="o">.</span><span class="n">handlers</span><span class="o">.</span><span class="n">DEFAULT_TCP_LOGGING_PORT</span><span class="p">)</span>
<span class="c1"># don't bother with a formatter, since a socket handler sends the event as</span>
<span class="c1"># an unformatted pickle</span>
<span class="n">rootLogger</span><span class="o">.</span><span class="n">addHandler</span><span class="p">(</span><span class="n">socketHandler</span><span class="p">)</span>
<span class="c1"># Now, we can log to the root logger, or any other logger. First the root...</span>
<span class="n">logging</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">'Jackdaws love my big sphinx of quartz.'</span><span class="p">)</span>
<span class="c1"># Now, define a couple of other loggers which might represent areas in your</span>
<span class="c1"># application:</span>
<span class="n">logger1</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="s1">'myapp.area1'</span><span class="p">)</span>
<span class="n">logger2</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="s1">'myapp.area2'</span><span class="p">)</span>
<span class="n">logger1</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s1">'Quick zephyrs blow, vexing daft Jim.'</span><span class="p">)</span>
<span class="n">logger1</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">'How quickly daft jumping zebras vex.'</span><span class="p">)</span>
<span class="n">logger2</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="s1">'Jail zesty vixen who grabbed pay from quack.'</span><span class="p">)</span>
<span class="n">logger2</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="s1">'The five boxing wizards jump quickly.'</span><span class="p">)</span>
</pre></div>
</div>
<p>At the receiving end, you can set up a receiver using the <a class="reference internal" href="../library/socketserver.html#module-socketserver" title="socketserver: A framework for network servers."><code class="xref py py-mod docutils literal notranslate"><span class="pre">socketserver</span></code></a>
module. Here is a basic working example:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">pickle</span>
<span class="kn">import</span> <span class="nn">logging</span>
<span class="kn">import</span> <span class="nn">logging.handlers</span>
<span class="kn">import</span> <span class="nn">socketserver</span>
<span class="kn">import</span> <span class="nn">struct</span>
<span class="k">class</span> <span class="nc">LogRecordStreamHandler</span><span class="p">(</span><span class="n">socketserver</span><span class="o">.</span><span class="n">StreamRequestHandler</span><span class="p">):</span>
<span class="sd">"""Handler for a streaming logging request.</span>
<span class="sd"> This basically logs the record using whatever logging policy is</span>
<span class="sd"> configured locally.</span>
<span class="sd"> """</span>
<span class="k">def</span> <span class="nf">handle</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""</span>
<span class="sd"> Handle multiple requests - each expected to be a 4-byte length,</span>
<span class="sd"> followed by the LogRecord in pickle format. Logs the record</span>
<span class="sd"> according to whatever policy is configured locally.</span>
<span class="sd"> """</span>
<span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
<span class="n">chunk</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">connection</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">chunk</span><span class="p">)</span> <span class="o"><</span> <span class="mi">4</span><span class="p">:</span>
<span class="k">break</span>
<span class="n">slen</span> <span class="o">=</span> <span class="n">struct</span><span class="o">.</span><span class="n">unpack</span><span class="p">(</span><span class="s1">'>L'</span><span class="p">,</span> <span class="n">chunk</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">chunk</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">connection</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="n">slen</span><span class="p">)</span>
<span class="k">while</span> <span class="nb">len</span><span class="p">(</span><span class="n">chunk</span><span class="p">)</span> <span class="o"><</span> <span class="n">slen</span><span class="p">:</span>
<span class="n">chunk</span> <span class="o">=</span> <span class="n">chunk</span> <span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">connection</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="n">slen</span> <span class="o">-</span> <span class="nb">len</span><span class="p">(</span><span class="n">chunk</span><span class="p">))</span>
<span class="n">obj</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">unPickle</span><span class="p">(</span><span class="n">chunk</span><span class="p">)</span>
<span class="n">record</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">makeLogRecord</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">handleLogRecord</span><span class="p">(</span><span class="n">record</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">unPickle</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">data</span><span class="p">):</span>
<span class="k">return</span> <span class="n">pickle</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">handleLogRecord</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">record</span><span class="p">):</span>
<span class="c1"># if a name is specified, we use the named logger rather than the one</span>
<span class="c1"># implied by the record.</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">server</span><span class="o">.</span><span class="n">logname</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">name</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">server</span><span class="o">.</span><span class="n">logname</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">name</span> <span class="o">=</span> <span class="n">record</span><span class="o">.</span><span class="n">name</span>
<span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
<span class="c1"># N.B. EVERY record gets logged. This is because Logger.handle</span>
<span class="c1"># is normally called AFTER logger-level filtering. If you want</span>
<span class="c1"># to do filtering, do it at the client end to save wasting</span>
<span class="c1"># cycles and network bandwidth!</span>
<span class="n">logger</span><span class="o">.</span><span class="n">handle</span><span class="p">(</span><span class="n">record</span><span class="p">)</span>
<span class="k">class</span> <span class="nc">LogRecordSocketReceiver</span><span class="p">(</span><span class="n">socketserver</span><span class="o">.</span><span class="n">ThreadingTCPServer</span><span class="p">):</span>
<span class="sd">"""</span>
<span class="sd"> Simple TCP socket-based logging receiver suitable for testing.</span>
<span class="sd"> """</span>
<span class="n">allow_reuse_address</span> <span class="o">=</span> <span class="kc">True</span>
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">host</span><span class="o">=</span><span class="s1">'localhost'</span><span class="p">,</span>
<span class="n">port</span><span class="o">=</span><span class="n">logging</span><span class="o">.</span><span class="n">handlers</span><span class="o">.</span><span class="n">DEFAULT_TCP_LOGGING_PORT</span><span class="p">,</span>
<span class="n">handler</span><span class="o">=</span><span class="n">LogRecordStreamHandler</span><span class="p">):</span>
<span class="n">socketserver</span><span class="o">.</span><span class="n">ThreadingTCPServer</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="p">(</span><span class="n">host</span><span class="p">,</span> <span class="n">port</span><span class="p">),</span> <span class="n">handler</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">abort</span> <span class="o">=</span> <span class="mi">0</span>
<span class="bp">self</span><span class="o">.</span><span class="n">timeout</span> <span class="o">=</span> <span class="mi">1</span>
<span class="bp">self</span><span class="o">.</span><span class="n">logname</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">def</span> <span class="nf">serve_until_stopped</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="kn">import</span> <span class="nn">select</span>
<span class="n">abort</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">while</span> <span class="ow">not</span> <span class="n">abort</span><span class="p">:</span>
<span class="n">rd</span><span class="p">,</span> <span class="n">wr</span><span class="p">,</span> <span class="n">ex</span> <span class="o">=</span> <span class="n">select</span><span class="o">.</span><span class="n">select</span><span class="p">([</span><span class="bp">self</span><span class="o">.</span><span class="n">socket</span><span class="o">.</span><span class="n">fileno</span><span class="p">()],</span>
<span class="p">[],</span> <span class="p">[],</span>
<span class="bp">self</span><span class="o">.</span><span class="n">timeout</span><span class="p">)</span>
<span class="k">if</span> <span class="n">rd</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">handle_request</span><span class="p">()</span>
<span class="n">abort</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">abort</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
<span class="n">logging</span><span class="o">.</span><span class="n">basicConfig</span><span class="p">(</span>
<span class="nb">format</span><span class="o">=</span><span class="s1">'</span><span class="si">%(relativeCreated)5d</span><span class="s1"> </span><span class="si">%(name)-15s</span><span class="s1"> </span><span class="si">%(levelname)-8s</span><span class="s1"> </span><span class="si">%(message)s</span><span class="s1">'</span><span class="p">)</span>
<span class="n">tcpserver</span> <span class="o">=</span> <span class="n">LogRecordSocketReceiver</span><span class="p">()</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'About to start TCP server...'</span><span class="p">)</span>
<span class="n">tcpserver</span><span class="o">.</span><span class="n">serve_until_stopped</span><span class="p">()</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">'__main__'</span><span class="p">:</span>
<span class="n">main</span><span class="p">()</span>
</pre></div>
</div>
<p>First run the server, and then the client. On the client side, nothing is
printed on the console; on the server side, you should see something like:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>About to start TCP server...
59 root INFO Jackdaws love my big sphinx of quartz.
59 myapp.area1 DEBUG Quick zephyrs blow, vexing daft Jim.
69 myapp.area1 INFO How quickly daft jumping zebras vex.
69 myapp.area2 WARNING Jail zesty vixen who grabbed pay from quack.
69 myapp.area2 ERROR The five boxing wizards jump quickly.
</pre></div>
</div>
<p>Note that there are some security issues with pickle in some scenarios. If
these affect you, you can use an alternative serialization scheme by overriding
the <code class="xref py py-meth docutils literal notranslate"><span class="pre">makePickle()</span></code> method and implementing your
alternative there, as well as adapting the above script to use your alternative
serialization.</p>
</div>
<div class="section" id="adding-contextual-information-to-your-logging-output">
<span id="context-info"></span><h2>Adding contextual information to your logging output<a class="headerlink" href="#adding-contextual-information-to-your-logging-output" title="本標題的永久連結">¶</a></h2>
<p>Sometimes you want logging output to contain contextual information in
addition to the parameters passed to the logging call. For example, in a
networked application, it may be desirable to log client-specific information
in the log (e.g. remote client’s username, or IP address). Although you could
use the <em>extra</em> parameter to achieve this, it’s not always convenient to pass
the information in this way. While it might be tempting to create
<code class="xref py py-class docutils literal notranslate"><span class="pre">Logger</span></code> instances on a per-connection basis, this is not a good idea
because these instances are not garbage collected. While this is not a problem
in practice, when the number of <code class="xref py py-class docutils literal notranslate"><span class="pre">Logger</span></code> instances is dependent on the
level of granularity you want to use in logging an application, it could
be hard to manage if the number of <code class="xref py py-class docutils literal notranslate"><span class="pre">Logger</span></code> instances becomes
effectively unbounded.</p>
<div class="section" id="using-loggeradapters-to-impart-contextual-information">
<h3>Using LoggerAdapters to impart contextual information<a class="headerlink" href="#using-loggeradapters-to-impart-contextual-information" title="本標題的永久連結">¶</a></h3>
<p>An easy way in which you can pass contextual information to be output along
with logging event information is to use the <code class="xref py py-class docutils literal notranslate"><span class="pre">LoggerAdapter</span></code> class.
This class is designed to look like a <code class="xref py py-class docutils literal notranslate"><span class="pre">Logger</span></code>, so that you can call
<code class="xref py py-meth docutils literal notranslate"><span class="pre">debug()</span></code>, <code class="xref py py-meth docutils literal notranslate"><span class="pre">info()</span></code>, <code class="xref py py-meth docutils literal notranslate"><span class="pre">warning()</span></code>, <code class="xref py py-meth docutils literal notranslate"><span class="pre">error()</span></code>,
<code class="xref py py-meth docutils literal notranslate"><span class="pre">exception()</span></code>, <code class="xref py py-meth docutils literal notranslate"><span class="pre">critical()</span></code> and <code class="xref py py-meth docutils literal notranslate"><span class="pre">log()</span></code>. These methods have the
same signatures as their counterparts in <code class="xref py py-class docutils literal notranslate"><span class="pre">Logger</span></code>, so you can use the
two types of instances interchangeably.</p>
<p>When you create an instance of <code class="xref py py-class docutils literal notranslate"><span class="pre">LoggerAdapter</span></code>, you pass it a
<code class="xref py py-class docutils literal notranslate"><span class="pre">Logger</span></code> instance and a dict-like object which contains your contextual
information. When you call one of the logging methods on an instance of
<code class="xref py py-class docutils literal notranslate"><span class="pre">LoggerAdapter</span></code>, it delegates the call to the underlying instance of
<code class="xref py py-class docutils literal notranslate"><span class="pre">Logger</span></code> passed to its constructor, and arranges to pass the contextual
information in the delegated call. Here’s a snippet from the code of
<code class="xref py py-class docutils literal notranslate"><span class="pre">LoggerAdapter</span></code>:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">debug</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">msg</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="sd">"""</span>
<span class="sd"> Delegate a debug call to the underlying logger, after adding</span>
<span class="sd"> contextual information from this adapter instance.</span>
<span class="sd"> """</span>
<span class="n">msg</span><span class="p">,</span> <span class="n">kwargs</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">process</span><span class="p">(</span><span class="n">msg</span><span class="p">,</span> <span class="n">kwargs</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">logger</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="n">msg</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</pre></div>
</div>
<p>The <code class="xref py py-meth docutils literal notranslate"><span class="pre">process()</span></code> method of <code class="xref py py-class docutils literal notranslate"><span class="pre">LoggerAdapter</span></code> is where the
contextual information is added to the logging output. It’s passed the message
and keyword arguments of the logging call, and it passes back (potentially)
modified versions of these to use in the call to the underlying logger. The
default implementation of this method leaves the message alone, but inserts
an 『extra』 key in the keyword argument whose value is the dict-like object
passed to the constructor. Of course, if you had passed an 『extra』 keyword
argument in the call to the adapter, it will be silently overwritten.</p>
<p>The advantage of using 『extra』 is that the values in the dict-like object are
merged into the <code class="xref py py-class docutils literal notranslate"><span class="pre">LogRecord</span></code> instance’s __dict__, allowing you to use
customized strings with your <code class="xref py py-class docutils literal notranslate"><span class="pre">Formatter</span></code> instances which know about
the keys of the dict-like object. If you need a different method, e.g. if you
want to prepend or append the contextual information to the message string,
you just need to subclass <code class="xref py py-class docutils literal notranslate"><span class="pre">LoggerAdapter</span></code> and override
<code class="xref py py-meth docutils literal notranslate"><span class="pre">process()</span></code> to do what you need. Here is a simple example:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">CustomAdapter</span><span class="p">(</span><span class="n">logging</span><span class="o">.</span><span class="n">LoggerAdapter</span><span class="p">):</span>
<span class="sd">"""</span>
<span class="sd"> This example adapter expects the passed in dict-like object to have a</span>
<span class="sd"> 'connid' key, whose value in brackets is prepended to the log message.</span>
<span class="sd"> """</span>
<span class="k">def</span> <span class="nf">process</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">msg</span><span class="p">,</span> <span class="n">kwargs</span><span class="p">):</span>
<span class="k">return</span> <span class="s1">'[</span><span class="si">%s</span><span class="s1">] </span><span class="si">%s</span><span class="s1">'</span> <span class="o">%</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">extra</span><span class="p">[</span><span class="s1">'connid'</span><span class="p">],</span> <span class="n">msg</span><span class="p">),</span> <span class="n">kwargs</span>
</pre></div>
</div>
<p>which you can use like this:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="vm">__name__</span><span class="p">)</span>
<span class="n">adapter</span> <span class="o">=</span> <span class="n">CustomAdapter</span><span class="p">(</span><span class="n">logger</span><span class="p">,</span> <span class="p">{</span><span class="s1">'connid'</span><span class="p">:</span> <span class="n">some_conn_id</span><span class="p">})</span>
</pre></div>
</div>
<p>Then any events that you log to the adapter will have the value of
<code class="docutils literal notranslate"><span class="pre">some_conn_id</span></code> prepended to the log messages.</p>
<div class="section" id="using-objects-other-than-dicts-to-pass-contextual-information">
<h4>Using objects other than dicts to pass contextual information<a class="headerlink" href="#using-objects-other-than-dicts-to-pass-contextual-information" title="本標題的永久連結">¶</a></h4>
<p>You don’t need to pass an actual dict to a <code class="xref py py-class docutils literal notranslate"><span class="pre">LoggerAdapter</span></code> - you could
pass an instance of a class which implements <code class="docutils literal notranslate"><span class="pre">__getitem__</span></code> and <code class="docutils literal notranslate"><span class="pre">__iter__</span></code> so
that it looks like a dict to logging. This would be useful if you want to
generate values dynamically (whereas the values in a dict would be constant).</p>
</div>
</div>
<div class="section" id="using-filters-to-impart-contextual-information">
<span id="filters-contextual"></span><h3>Using Filters to impart contextual information<a class="headerlink" href="#using-filters-to-impart-contextual-information" title="本標題的永久連結">¶</a></h3>
<p>You can also add contextual information to log output using a user-defined
<code class="xref py py-class docutils literal notranslate"><span class="pre">Filter</span></code>. <code class="docutils literal notranslate"><span class="pre">Filter</span></code> instances are allowed to modify the <code class="docutils literal notranslate"><span class="pre">LogRecords</span></code>
passed to them, including adding additional attributes which can then be output
using a suitable format string, or if needed a custom <code class="xref py py-class docutils literal notranslate"><span class="pre">Formatter</span></code>.</p>
<p>For example in a web application, the request being processed (or at least,
the interesting parts of it) can be stored in a threadlocal
(<a class="reference internal" href="../library/threading.html#threading.local" title="threading.local"><code class="xref py py-class docutils literal notranslate"><span class="pre">threading.local</span></code></a>) variable, and then accessed from a <code class="docutils literal notranslate"><span class="pre">Filter</span></code> to
add, say, information from the request - say, the remote IP address and remote
user’s username - to the <code class="docutils literal notranslate"><span class="pre">LogRecord</span></code>, using the attribute names 『ip』 and
『user』 as in the <code class="docutils literal notranslate"><span class="pre">LoggerAdapter</span></code> example above. In that case, the same format
string can be used to get similar output to that shown above. Here’s an example
script:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">logging</span>
<span class="kn">from</span> <span class="nn">random</span> <span class="k">import</span> <span class="n">choice</span>
<span class="k">class</span> <span class="nc">ContextFilter</span><span class="p">(</span><span class="n">logging</span><span class="o">.</span><span class="n">Filter</span><span class="p">):</span>
<span class="sd">"""</span>
<span class="sd"> This is a filter which injects contextual information into the log.</span>
<span class="sd"> Rather than use actual contextual information, we just use random</span>
<span class="sd"> data in this demo.</span>
<span class="sd"> """</span>
<span class="n">USERS</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'jim'</span><span class="p">,</span> <span class="s1">'fred'</span><span class="p">,</span> <span class="s1">'sheila'</span><span class="p">]</span>
<span class="n">IPS</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'123.231.231.123'</span><span class="p">,</span> <span class="s1">'127.0.0.1'</span><span class="p">,</span> <span class="s1">'192.168.0.1'</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">filter</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">record</span><span class="p">):</span>
<span class="n">record</span><span class="o">.</span><span class="n">ip</span> <span class="o">=</span> <span class="n">choice</span><span class="p">(</span><span class="n">ContextFilter</span><span class="o">.</span><span class="n">IPS</span><span class="p">)</span>
<span class="n">record</span><span class="o">.</span><span class="n">user</span> <span class="o">=</span> <span class="n">choice</span><span class="p">(</span><span class="n">ContextFilter</span><span class="o">.</span><span class="n">USERS</span><span class="p">)</span>
<span class="k">return</span> <span class="kc">True</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">'__main__'</span><span class="p">:</span>
<span class="n">levels</span> <span class="o">=</span> <span class="p">(</span><span class="n">logging</span><span class="o">.</span><span class="n">DEBUG</span><span class="p">,</span> <span class="n">logging</span><span class="o">.</span><span class="n">INFO</span><span class="p">,</span> <span class="n">logging</span><span class="o">.</span><span class="n">WARNING</span><span class="p">,</span> <span class="n">logging</span><span class="o">.</span><span class="n">ERROR</span><span class="p">,</span> <span class="n">logging</span><span class="o">.</span><span class="n">CRITICAL</span><span class="p">)</span>
<span class="n">logging</span><span class="o">.</span><span class="n">basicConfig</span><span class="p">(</span><span class="n">level</span><span class="o">=</span><span class="n">logging</span><span class="o">.</span><span class="n">DEBUG</span><span class="p">,</span>
<span class="nb">format</span><span class="o">=</span><span class="s1">'</span><span class="si">%(asctime)-15s</span><span class="s1"> </span><span class="si">%(name)-5s</span><span class="s1"> </span><span class="si">%(levelname)-8s</span><span class="s1"> IP: </span><span class="si">%(ip)-15s</span><span class="s1"> User: </span><span class="si">%(user)-8s</span><span class="s1"> </span><span class="si">%(message)s</span><span class="s1">'</span><span class="p">)</span>
<span class="n">a1</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="s1">'a.b.c'</span><span class="p">)</span>
<span class="n">a2</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="s1">'d.e.f'</span><span class="p">)</span>
<span class="n">f</span> <span class="o">=</span> <span class="n">ContextFilter</span><span class="p">()</span>
<span class="n">a1</span><span class="o">.</span><span class="n">addFilter</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="n">a2</span><span class="o">.</span><span class="n">addFilter</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="n">a1</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s1">'A debug message'</span><span class="p">)</span>
<span class="n">a1</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">'An info message with </span><span class="si">%s</span><span class="s1">'</span><span class="p">,</span> <span class="s1">'some parameters'</span><span class="p">)</span>
<span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">):</span>
<span class="n">lvl</span> <span class="o">=</span> <span class="n">choice</span><span class="p">(</span><span class="n">levels</span><span class="p">)</span>
<span class="n">lvlname</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLevelName</span><span class="p">(</span><span class="n">lvl</span><span class="p">)</span>
<span class="n">a2</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="n">lvl</span><span class="p">,</span> <span class="s1">'A message at </span><span class="si">%s</span><span class="s1"> level with </span><span class="si">%d</span><span class="s1"> </span><span class="si">%s</span><span class="s1">'</span><span class="p">,</span> <span class="n">lvlname</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="s1">'parameters'</span><span class="p">)</span>
</pre></div>
</div>
<p>which, when run, produces something like:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>2010-09-06 22:38:15,292 a.b.c DEBUG IP: 123.231.231.123 User: fred A debug message
2010-09-06 22:38:15,300 a.b.c INFO IP: 192.168.0.1 User: sheila An info message with some parameters
2010-09-06 22:38:15,300 d.e.f CRITICAL IP: 127.0.0.1 User: sheila A message at CRITICAL level with 2 parameters
2010-09-06 22:38:15,300 d.e.f ERROR IP: 127.0.0.1 User: jim A message at ERROR level with 2 parameters
2010-09-06 22:38:15,300 d.e.f DEBUG IP: 127.0.0.1 User: sheila A message at DEBUG level with 2 parameters
2010-09-06 22:38:15,300 d.e.f ERROR IP: 123.231.231.123 User: fred A message at ERROR level with 2 parameters
2010-09-06 22:38:15,300 d.e.f CRITICAL IP: 192.168.0.1 User: jim A message at CRITICAL level with 2 parameters
2010-09-06 22:38:15,300 d.e.f CRITICAL IP: 127.0.0.1 User: sheila A message at CRITICAL level with 2 parameters
2010-09-06 22:38:15,300 d.e.f DEBUG IP: 192.168.0.1 User: jim A message at DEBUG level with 2 parameters
2010-09-06 22:38:15,301 d.e.f ERROR IP: 127.0.0.1 User: sheila A message at ERROR level with 2 parameters
2010-09-06 22:38:15,301 d.e.f DEBUG IP: 123.231.231.123 User: fred A message at DEBUG level with 2 parameters
2010-09-06 22:38:15,301 d.e.f INFO IP: 123.231.231.123 User: fred A message at INFO level with 2 parameters
</pre></div>
</div>
</div>
</div>
<div class="section" id="logging-to-a-single-file-from-multiple-processes">
<span id="multiple-processes"></span><h2>Logging to a single file from multiple processes<a class="headerlink" href="#logging-to-a-single-file-from-multiple-processes" title="本標題的永久連結">¶</a></h2>
<p>Although logging is thread-safe, and logging to a single file from multiple
threads in a single process <em>is</em> supported, logging to a single file from
<em>multiple processes</em> is <em>not</em> supported, because there is no standard way to
serialize access to a single file across multiple processes in Python. If you
need to log to a single file from multiple processes, one way of doing this is
to have all the processes log to a <code class="xref py py-class docutils literal notranslate"><span class="pre">SocketHandler</span></code>, and have a
separate process which implements a socket server which reads from the socket
and logs to file. (If you prefer, you can dedicate one thread in one of the
existing processes to perform this function.)
<a class="reference internal" href="#network-logging"><span class="std std-ref">This section</span></a> documents this approach in more detail and
includes a working socket receiver which can be used as a starting point for you
to adapt in your own applications.</p>
<p>If you are using a recent version of Python which includes the
<a class="reference internal" href="../library/multiprocessing.html#module-multiprocessing" title="multiprocessing: Process-based parallelism."><code class="xref py py-mod docutils literal notranslate"><span class="pre">multiprocessing</span></code></a> module, you could write your own handler which uses the
<a class="reference internal" href="../library/multiprocessing.html#multiprocessing.Lock" title="multiprocessing.Lock"><code class="xref py py-class docutils literal notranslate"><span class="pre">Lock</span></code></a> class from this module to serialize access to the
file from your processes. The existing <code class="xref py py-class docutils literal notranslate"><span class="pre">FileHandler</span></code> and subclasses do
not make use of <a class="reference internal" href="../library/multiprocessing.html#module-multiprocessing" title="multiprocessing: Process-based parallelism."><code class="xref py py-mod docutils literal notranslate"><span class="pre">multiprocessing</span></code></a> at present, though they may do so in the
future. Note that at present, the <a class="reference internal" href="../library/multiprocessing.html#module-multiprocessing" title="multiprocessing: Process-based parallelism."><code class="xref py py-mod docutils literal notranslate"><span class="pre">multiprocessing</span></code></a> module does not provide
working lock functionality on all platforms (see
<a class="reference external" href="https://bugs.python.org/issue3770">https://bugs.python.org/issue3770</a>).</p>
<p>Alternatively, you can use a <code class="docutils literal notranslate"><span class="pre">Queue</span></code> and a <a class="reference internal" href="../library/logging.handlers.html#logging.handlers.QueueHandler" title="logging.handlers.QueueHandler"><code class="xref py py-class docutils literal notranslate"><span class="pre">QueueHandler</span></code></a> to send
all logging events to one of the processes in your multi-process application.
The following example script demonstrates how you can do this; in the example
a separate listener process listens for events sent by other processes and logs
them according to its own logging configuration. Although the example only
demonstrates one way of doing it (for example, you may want to use a listener
thread rather than a separate listener process – the implementation would be
analogous) it does allow for completely different logging configurations for
the listener and the other processes in your application, and can be used as
the basis for code meeting your own specific requirements:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="c1"># You'll need these imports in your own code</span>
<span class="kn">import</span> <span class="nn">logging</span>
<span class="kn">import</span> <span class="nn">logging.handlers</span>
<span class="kn">import</span> <span class="nn">multiprocessing</span>
<span class="c1"># Next two import lines for this demo only</span>
<span class="kn">from</span> <span class="nn">random</span> <span class="k">import</span> <span class="n">choice</span><span class="p">,</span> <span class="n">random</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="c1">#</span>
<span class="c1"># Because you'll want to define the logging configurations for listener and workers, the</span>
<span class="c1"># listener and worker process functions take a configurer parameter which is a callable</span>
<span class="c1"># for configuring logging for that process. These functions are also passed the queue,</span>
<span class="c1"># which they use for communication.</span>
<span class="c1">#</span>
<span class="c1"># In practice, you can configure the listener however you want, but note that in this</span>
<span class="c1"># simple example, the listener does not apply level or filter logic to received records.</span>
<span class="c1"># In practice, you would probably want to do this logic in the worker processes, to avoid</span>
<span class="c1"># sending events which would be filtered out between processes.</span>
<span class="c1">#</span>
<span class="c1"># The size of the rotated files is made small so you can see the results easily.</span>
<span class="k">def</span> <span class="nf">listener_configurer</span><span class="p">():</span>
<span class="n">root</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">()</span>
<span class="n">h</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">handlers</span><span class="o">.</span><span class="n">RotatingFileHandler</span><span class="p">(</span><span class="s1">'mptest.log'</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">,</span> <span class="mi">300</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span>
<span class="n">f</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">Formatter</span><span class="p">(</span><span class="s1">'</span><span class="si">%(asctime)s</span><span class="s1"> </span><span class="si">%(processName)-10s</span><span class="s1"> </span><span class="si">%(name)s</span><span class="s1"> </span><span class="si">%(levelname)-8s</span><span class="s1"> </span><span class="si">%(message)s</span><span class="s1">'</span><span class="p">)</span>
<span class="n">h</span><span class="o">.</span><span class="n">setFormatter</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="n">root</span><span class="o">.</span><span class="n">addHandler</span><span class="p">(</span><span class="n">h</span><span class="p">)</span>
<span class="c1"># This is the listener process top-level loop: wait for logging events</span>
<span class="c1"># (LogRecords)on the queue and handle them, quit when you get a None for a</span>
<span class="c1"># LogRecord.</span>
<span class="k">def</span> <span class="nf">listener_process</span><span class="p">(</span><span class="n">queue</span><span class="p">,</span> <span class="n">configurer</span><span class="p">):</span>
<span class="n">configurer</span><span class="p">()</span>
<span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">record</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">get</span><span class="p">()</span>
<span class="k">if</span> <span class="n">record</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span> <span class="c1"># We send this as a sentinel to tell the listener to quit.</span>
<span class="k">break</span>
<span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="n">record</span><span class="o">.</span><span class="n">name</span><span class="p">)</span>
<span class="n">logger</span><span class="o">.</span><span class="n">handle</span><span class="p">(</span><span class="n">record</span><span class="p">)</span> <span class="c1"># No level or filter logic applied - just do it!</span>
<span class="k">except</span> <span class="ne">Exception</span><span class="p">:</span>
<span class="kn">import</span> <span class="nn">sys</span><span class="o">,</span> <span class="nn">traceback</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'Whoops! Problem:'</span><span class="p">,</span> <span class="n">file</span><span class="o">=</span><span class="n">sys</span><span class="o">.</span><span class="n">stderr</span><span class="p">)</span>
<span class="n">traceback</span><span class="o">.</span><span class="n">print_exc</span><span class="p">(</span><span class="n">file</span><span class="o">=</span><span class="n">sys</span><span class="o">.</span><span class="n">stderr</span><span class="p">)</span>
<span class="c1"># Arrays used for random selections in this demo</span>
<span class="n">LEVELS</span> <span class="o">=</span> <span class="p">[</span><span class="n">logging</span><span class="o">.</span><span class="n">DEBUG</span><span class="p">,</span> <span class="n">logging</span><span class="o">.</span><span class="n">INFO</span><span class="p">,</span> <span class="n">logging</span><span class="o">.</span><span class="n">WARNING</span><span class="p">,</span>
<span class="n">logging</span><span class="o">.</span><span class="n">ERROR</span><span class="p">,</span> <span class="n">logging</span><span class="o">.</span><span class="n">CRITICAL</span><span class="p">]</span>
<span class="n">LOGGERS</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'a.b.c'</span><span class="p">,</span> <span class="s1">'d.e.f'</span><span class="p">]</span>
<span class="n">MESSAGES</span> <span class="o">=</span> <span class="p">[</span>
<span class="s1">'Random message #1'</span><span class="p">,</span>
<span class="s1">'Random message #2'</span><span class="p">,</span>
<span class="s1">'Random message #3'</span><span class="p">,</span>
<span class="p">]</span>
<span class="c1"># The worker configuration is done at the start of the worker process run.</span>
<span class="c1"># Note that on Windows you can't rely on fork semantics, so each process</span>
<span class="c1"># will run the logging configuration code when it starts.</span>
<span class="k">def</span> <span class="nf">worker_configurer</span><span class="p">(</span><span class="n">queue</span><span class="p">):</span>
<span class="n">h</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">handlers</span><span class="o">.</span><span class="n">QueueHandler</span><span class="p">(</span><span class="n">queue</span><span class="p">)</span> <span class="c1"># Just the one handler needed</span>
<span class="n">root</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">()</span>
<span class="n">root</span><span class="o">.</span><span class="n">addHandler</span><span class="p">(</span><span class="n">h</span><span class="p">)</span>
<span class="c1"># send all messages, for demo; no other level or filter logic applied.</span>
<span class="n">root</span><span class="o">.</span><span class="n">setLevel</span><span class="p">(</span><span class="n">logging</span><span class="o">.</span><span class="n">DEBUG</span><span class="p">)</span>
<span class="c1"># This is the worker process top-level loop, which just logs ten events with</span>
<span class="c1"># random intervening delays before terminating.</span>
<span class="c1"># The print messages are just so you know it's doing something!</span>
<span class="k">def</span> <span class="nf">worker_process</span><span class="p">(</span><span class="n">queue</span><span class="p">,</span> <span class="n">configurer</span><span class="p">):</span>
<span class="n">configurer</span><span class="p">(</span><span class="n">queue</span><span class="p">)</span>
<span class="n">name</span> <span class="o">=</span> <span class="n">multiprocessing</span><span class="o">.</span><span class="n">current_process</span><span class="p">()</span><span class="o">.</span><span class="n">name</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'Worker started: </span><span class="si">%s</span><span class="s1">'</span> <span class="o">%</span> <span class="n">name</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">):</span>
<span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="n">random</span><span class="p">())</span>
<span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="n">choice</span><span class="p">(</span><span class="n">LOGGERS</span><span class="p">))</span>
<span class="n">level</span> <span class="o">=</span> <span class="n">choice</span><span class="p">(</span><span class="n">LEVELS</span><span class="p">)</span>
<span class="n">message</span> <span class="o">=</span> <span class="n">choice</span><span class="p">(</span><span class="n">MESSAGES</span><span class="p">)</span>
<span class="n">logger</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="n">level</span><span class="p">,</span> <span class="n">message</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'Worker finished: </span><span class="si">%s</span><span class="s1">'</span> <span class="o">%</span> <span class="n">name</span><span class="p">)</span>
<span class="c1"># Here's where the demo gets orchestrated. Create the queue, create and start</span>
<span class="c1"># the listener, create ten workers and start them, wait for them to finish,</span>
<span class="c1"># then send a None to the queue to tell the listener to finish.</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
<span class="n">queue</span> <span class="o">=</span> <span class="n">multiprocessing</span><span class="o">.</span><span class="n">Queue</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span>
<span class="n">listener</span> <span class="o">=</span> <span class="n">multiprocessing</span><span class="o">.</span><span class="n">Process</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">listener_process</span><span class="p">,</span>
<span class="n">args</span><span class="o">=</span><span class="p">(</span><span class="n">queue</span><span class="p">,</span> <span class="n">listener_configurer</span><span class="p">))</span>
<span class="n">listener</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
<span class="n">workers</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">):</span>
<span class="n">worker</span> <span class="o">=</span> <span class="n">multiprocessing</span><span class="o">.</span><span class="n">Process</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">worker_process</span><span class="p">,</span>
<span class="n">args</span><span class="o">=</span><span class="p">(</span><span class="n">queue</span><span class="p">,</span> <span class="n">worker_configurer</span><span class="p">))</span>
<span class="n">workers</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">worker</span><span class="p">)</span>
<span class="n">worker</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
<span class="k">for</span> <span class="n">w</span> <span class="ow">in</span> <span class="n">workers</span><span class="p">:</span>
<span class="n">w</span><span class="o">.</span><span class="n">join</span><span class="p">()</span>
<span class="n">queue</span><span class="o">.</span><span class="n">put_nowait</span><span class="p">(</span><span class="kc">None</span><span class="p">)</span>
<span class="n">listener</span><span class="o">.</span><span class="n">join</span><span class="p">()</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">'__main__'</span><span class="p">:</span>
<span class="n">main</span><span class="p">()</span>
</pre></div>
</div>
<p>A variant of the above script keeps the logging in the main process, in a
separate thread:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">logging</span>
<span class="kn">import</span> <span class="nn">logging.config</span>
<span class="kn">import</span> <span class="nn">logging.handlers</span>
<span class="kn">from</span> <span class="nn">multiprocessing</span> <span class="k">import</span> <span class="n">Process</span><span class="p">,</span> <span class="n">Queue</span>
<span class="kn">import</span> <span class="nn">random</span>
<span class="kn">import</span> <span class="nn">threading</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="k">def</span> <span class="nf">logger_thread</span><span class="p">(</span><span class="n">q</span><span class="p">):</span>
<span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
<span class="n">record</span> <span class="o">=</span> <span class="n">q</span><span class="o">.</span><span class="n">get</span><span class="p">()</span>
<span class="k">if</span> <span class="n">record</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">break</span>
<span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="n">record</span><span class="o">.</span><span class="n">name</span><span class="p">)</span>
<span class="n">logger</span><span class="o">.</span><span class="n">handle</span><span class="p">(</span><span class="n">record</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">worker_process</span><span class="p">(</span><span class="n">q</span><span class="p">):</span>
<span class="n">qh</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">handlers</span><span class="o">.</span><span class="n">QueueHandler</span><span class="p">(</span><span class="n">q</span><span class="p">)</span>
<span class="n">root</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">()</span>
<span class="n">root</span><span class="o">.</span><span class="n">setLevel</span><span class="p">(</span><span class="n">logging</span><span class="o">.</span><span class="n">DEBUG</span><span class="p">)</span>
<span class="n">root</span><span class="o">.</span><span class="n">addHandler</span><span class="p">(</span><span class="n">qh</span><span class="p">)</span>
<span class="n">levels</span> <span class="o">=</span> <span class="p">[</span><span class="n">logging</span><span class="o">.</span><span class="n">DEBUG</span><span class="p">,</span> <span class="n">logging</span><span class="o">.</span><span class="n">INFO</span><span class="p">,</span> <span class="n">logging</span><span class="o">.</span><span class="n">WARNING</span><span class="p">,</span> <span class="n">logging</span><span class="o">.</span><span class="n">ERROR</span><span class="p">,</span>
<span class="n">logging</span><span class="o">.</span><span class="n">CRITICAL</span><span class="p">]</span>
<span class="n">loggers</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'foo'</span><span class="p">,</span> <span class="s1">'foo.bar'</span><span class="p">,</span> <span class="s1">'foo.bar.baz'</span><span class="p">,</span>
<span class="s1">'spam'</span><span class="p">,</span> <span class="s1">'spam.ham'</span><span class="p">,</span> <span class="s1">'spam.ham.eggs'</span><span class="p">]</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">100</span><span class="p">):</span>
<span class="n">lvl</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">levels</span><span class="p">)</span>
<span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">loggers</span><span class="p">))</span>
<span class="n">logger</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="n">lvl</span><span class="p">,</span> <span class="s1">'Message no. </span><span class="si">%d</span><span class="s1">'</span><span class="p">,</span> <span class="n">i</span><span class="p">)</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">'__main__'</span><span class="p">:</span>
<span class="n">q</span> <span class="o">=</span> <span class="n">Queue</span><span class="p">()</span>
<span class="n">d</span> <span class="o">=</span> <span class="p">{</span>
<span class="s1">'version'</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
<span class="s1">'formatters'</span><span class="p">:</span> <span class="p">{</span>
<span class="s1">'detailed'</span><span class="p">:</span> <span class="p">{</span>
<span class="s1">'class'</span><span class="p">:</span> <span class="s1">'logging.Formatter'</span><span class="p">,</span>
<span class="s1">'format'</span><span class="p">:</span> <span class="s1">'</span><span class="si">%(asctime)s</span><span class="s1"> </span><span class="si">%(name)-15s</span><span class="s1"> </span><span class="si">%(levelname)-8s</span><span class="s1"> </span><span class="si">%(processName)-10s</span><span class="s1"> </span><span class="si">%(message)s</span><span class="s1">'</span>
<span class="p">}</span>
<span class="p">},</span>
<span class="s1">'handlers'</span><span class="p">:</span> <span class="p">{</span>
<span class="s1">'console'</span><span class="p">:</span> <span class="p">{</span>
<span class="s1">'class'</span><span class="p">:</span> <span class="s1">'logging.StreamHandler'</span><span class="p">,</span>
<span class="s1">'level'</span><span class="p">:</span> <span class="s1">'INFO'</span><span class="p">,</span>
<span class="p">},</span>
<span class="s1">'file'</span><span class="p">:</span> <span class="p">{</span>
<span class="s1">'class'</span><span class="p">:</span> <span class="s1">'logging.FileHandler'</span><span class="p">,</span>
<span class="s1">'filename'</span><span class="p">:</span> <span class="s1">'mplog.log'</span><span class="p">,</span>
<span class="s1">'mode'</span><span class="p">:</span> <span class="s1">'w'</span><span class="p">,</span>
<span class="s1">'formatter'</span><span class="p">:</span> <span class="s1">'detailed'</span><span class="p">,</span>
<span class="p">},</span>
<span class="s1">'foofile'</span><span class="p">:</span> <span class="p">{</span>
<span class="s1">'class'</span><span class="p">:</span> <span class="s1">'logging.FileHandler'</span><span class="p">,</span>
<span class="s1">'filename'</span><span class="p">:</span> <span class="s1">'mplog-foo.log'</span><span class="p">,</span>
<span class="s1">'mode'</span><span class="p">:</span> <span class="s1">'w'</span><span class="p">,</span>
<span class="s1">'formatter'</span><span class="p">:</span> <span class="s1">'detailed'</span><span class="p">,</span>
<span class="p">},</span>
<span class="s1">'errors'</span><span class="p">:</span> <span class="p">{</span>
<span class="s1">'class'</span><span class="p">:</span> <span class="s1">'logging.FileHandler'</span><span class="p">,</span>
<span class="s1">'filename'</span><span class="p">:</span> <span class="s1">'mplog-errors.log'</span><span class="p">,</span>
<span class="s1">'mode'</span><span class="p">:</span> <span class="s1">'w'</span><span class="p">,</span>
<span class="s1">'level'</span><span class="p">:</span> <span class="s1">'ERROR'</span><span class="p">,</span>
<span class="s1">'formatter'</span><span class="p">:</span> <span class="s1">'detailed'</span><span class="p">,</span>
<span class="p">},</span>
<span class="p">},</span>
<span class="s1">'loggers'</span><span class="p">:</span> <span class="p">{</span>
<span class="s1">'foo'</span><span class="p">:</span> <span class="p">{</span>
<span class="s1">'handlers'</span><span class="p">:</span> <span class="p">[</span><span class="s1">'foofile'</span><span class="p">]</span>
<span class="p">}</span>
<span class="p">},</span>
<span class="s1">'root'</span><span class="p">:</span> <span class="p">{</span>
<span class="s1">'level'</span><span class="p">:</span> <span class="s1">'DEBUG'</span><span class="p">,</span>
<span class="s1">'handlers'</span><span class="p">:</span> <span class="p">[</span><span class="s1">'console'</span><span class="p">,</span> <span class="s1">'file'</span><span class="p">,</span> <span class="s1">'errors'</span><span class="p">]</span>
<span class="p">},</span>
<span class="p">}</span>
<span class="n">workers</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">):</span>
<span class="n">wp</span> <span class="o">=</span> <span class="n">Process</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">worker_process</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s1">'worker </span><span class="si">%d</span><span class="s1">'</span> <span class="o">%</span> <span class="p">(</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">),</span> <span class="n">args</span><span class="o">=</span><span class="p">(</span><span class="n">q</span><span class="p">,))</span>
<span class="n">workers</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">wp</span><span class="p">)</span>
<span class="n">wp</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
<span class="n">logging</span><span class="o">.</span><span class="n">config</span><span class="o">.</span><span class="n">dictConfig</span><span class="p">(</span><span class="n">d</span><span class="p">)</span>
<span class="n">lp</span> <span class="o">=</span> <span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">logger_thread</span><span class="p">,</span> <span class="n">args</span><span class="o">=</span><span class="p">(</span><span class="n">q</span><span class="p">,))</span>
<span class="n">lp</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
<span class="c1"># At this point, the main process could do some useful work of its own</span>
<span class="c1"># Once it's done that, it can wait for the workers to terminate...</span>
<span class="k">for</span> <span class="n">wp</span> <span class="ow">in</span> <span class="n">workers</span><span class="p">:</span>
<span class="n">wp</span><span class="o">.</span><span class="n">join</span><span class="p">()</span>
<span class="c1"># And now tell the logging thread to finish up, too</span>
<span class="n">q</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="kc">None</span><span class="p">)</span>
<span class="n">lp</span><span class="o">.</span><span class="n">join</span><span class="p">()</span>
</pre></div>
</div>
<p>This variant shows how you can e.g. apply configuration for particular loggers
- e.g. the <code class="docutils literal notranslate"><span class="pre">foo</span></code> logger has a special handler which stores all events in the
<code class="docutils literal notranslate"><span class="pre">foo</span></code> subsystem in a file <code class="docutils literal notranslate"><span class="pre">mplog-foo.log</span></code>. This will be used by the logging
machinery in the main process (even though the logging events are generated in
the worker processes) to direct the messages to the appropriate destinations.</p>
</div>
<div class="section" id="using-file-rotation">
<h2>Using file rotation<a class="headerlink" href="#using-file-rotation" title="本標題的永久連結">¶</a></h2>
<p>Sometimes you want to let a log file grow to a certain size, then open a new
file and log to that. You may want to keep a certain number of these files, and
when that many files have been created, rotate the files so that the number of
files and the size of the files both remain bounded. For this usage pattern, the
logging package provides a <code class="xref py py-class docutils literal notranslate"><span class="pre">RotatingFileHandler</span></code>:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">glob</span>
<span class="kn">import</span> <span class="nn">logging</span>
<span class="kn">import</span> <span class="nn">logging.handlers</span>
<span class="n">LOG_FILENAME</span> <span class="o">=</span> <span class="s1">'logging_rotatingfile_example.out'</span>
<span class="c1"># Set up a specific logger with our desired output level</span>
<span class="n">my_logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="s1">'MyLogger'</span><span class="p">)</span>
<span class="n">my_logger</span><span class="o">.</span><span class="n">setLevel</span><span class="p">(</span><span class="n">logging</span><span class="o">.</span><span class="n">DEBUG</span><span class="p">)</span>
<span class="c1"># Add the log message handler to the logger</span>
<span class="n">handler</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">handlers</span><span class="o">.</span><span class="n">RotatingFileHandler</span><span class="p">(</span>
<span class="n">LOG_FILENAME</span><span class="p">,</span> <span class="n">maxBytes</span><span class="o">=</span><span class="mi">20</span><span class="p">,</span> <span class="n">backupCount</span><span class="o">=</span><span class="mi">5</span><span class="p">)</span>