5
5
6
6
import asyncio
7
7
import json
8
+ import logging
8
9
import os
9
10
import ssl
10
11
import sys
18
19
from tornado .httpclient import AsyncHTTPClient , HTTPRequest
19
20
from tornado .httputil import url_concat
20
21
from tornado .ioloop import IOLoop , PeriodicCallback
21
- from tornado .log import app_log
22
+ from tornado .log import LogFormatter
22
23
from traitlets import Bool , Int , Unicode , default
23
24
from traitlets .config import Application
24
25
@@ -81,6 +82,7 @@ async def cull_idle(
81
82
url ,
82
83
api_token ,
83
84
inactive_limit ,
85
+ logger ,
84
86
cull_users = False ,
85
87
remove_named_servers = False ,
86
88
max_age = 0 ,
@@ -109,8 +111,8 @@ async def cull_idle(
109
111
f"{ internal_certs_location } /hub-ca/hub-ca.crt" ,
110
112
)
111
113
112
- app_log .debug ("ssl_enabled is Enabled: %s" , ssl_enabled )
113
- app_log .debug ("internal_certs_location is %s" , internal_certs_location )
114
+ logger .debug ("ssl_enabled is Enabled: %s" , ssl_enabled )
115
+ logger .debug ("internal_certs_location is %s" , internal_certs_location )
114
116
defaults ["ssl_options" ] = ssl_context
115
117
116
118
AsyncHTTPClient .configure (None , defaults = defaults )
@@ -155,7 +157,7 @@ async def fetch_paginated(req):
155
157
next_info = resp_model ["_pagination" ]["next" ]
156
158
if next_info :
157
159
page_no += 1
158
- app_log .info (f"Fetching page { page_no } { next_info ['url' ]} " )
160
+ logger .info (f"Fetching page { page_no } { next_info ['url' ]} " )
159
161
# submit next request
160
162
req .url = next_info ["url" ]
161
163
resp_future = asyncio .ensure_future (fetch (req ))
@@ -164,7 +166,7 @@ async def fetch_paginated(req):
164
166
item_count += 1
165
167
yield item
166
168
167
- app_log .debug (f"Fetched { item_count } items from { url } in { page_no } pages" )
169
+ logger .debug (f"Fetched { item_count } items from { url } in { page_no } pages" )
168
170
169
171
# Starting with jupyterhub 1.3.0 the users can be filtered in the server
170
172
# using the `state` filter parameter. "ready" means all users who have any
@@ -189,7 +191,7 @@ async def handle_server(user, server_name, server, max_age, inactive_limit):
189
191
if server_name :
190
192
log_name = f"{ user ['name' ]} /{ server_name } "
191
193
if server .get ("pending" ):
192
- app_log .warning (
194
+ logger .warning (
193
195
f"Not culling server { log_name } with pending { server ['pending' ]} "
194
196
)
195
197
return False
@@ -202,7 +204,7 @@ async def handle_server(user, server_name, server, max_age, inactive_limit):
202
204
# but let's check just to be safe.
203
205
204
206
if not server .get ("ready" , bool (server ["url" ])):
205
- app_log .warning (
207
+ logger .warning (
206
208
f"Not culling not-ready not-pending server { log_name } : { server } "
207
209
)
208
210
return False
@@ -250,7 +252,7 @@ async def handle_server(user, server_name, server, max_age, inactive_limit):
250
252
)
251
253
)
252
254
if should_cull :
253
- app_log .info (
255
+ logger .info (
254
256
f"Culling server { log_name } (inactive for { format_td (inactive )} )"
255
257
)
256
258
@@ -259,7 +261,7 @@ async def handle_server(user, server_name, server, max_age, inactive_limit):
259
261
# so that we can still be compatible with jupyterhub 0.8
260
262
# which doesn't define the 'started' field
261
263
if age is not None and age .total_seconds () >= max_age :
262
- app_log .info (
264
+ logger .info (
263
265
"Culling server %s (age: %s, inactive for %s)" ,
264
266
log_name ,
265
267
format_td (age ),
@@ -268,7 +270,7 @@ async def handle_server(user, server_name, server, max_age, inactive_limit):
268
270
should_cull = True
269
271
270
272
if not should_cull :
271
- app_log .debug (
273
+ logger .debug (
272
274
"Not culling server %s (age: %s, inactive for %s)" ,
273
275
log_name ,
274
276
format_td (age ),
@@ -305,7 +307,7 @@ async def handle_server(user, server_name, server, max_age, inactive_limit):
305
307
)
306
308
resp = await fetch (req )
307
309
if resp .code == 202 :
308
- app_log .warning (f"Server { log_name } is slow to stop" )
310
+ logger .warning (f"Server { log_name } is slow to stop" )
309
311
# return False to prevent culling user with pending shutdowns
310
312
return False
311
313
return True
@@ -349,7 +351,7 @@ async def handle_user(user):
349
351
# some servers are still running, cannot cull users
350
352
still_alive = len (results ) - sum (results )
351
353
if still_alive :
352
- app_log .debug (
354
+ logger .debug (
353
355
"Not culling user %s with %i servers still alive" ,
354
356
user ["name" ],
355
357
still_alive ,
@@ -380,21 +382,21 @@ async def handle_user(user):
380
382
) and (cull_admin_users or not user_is_admin )
381
383
382
384
if should_cull :
383
- app_log .info (f"Culling user { user ['name' ]} " f"(inactive for { inactive } )" )
385
+ logger .info (f"Culling user { user ['name' ]} " f"(inactive for { inactive } )" )
384
386
385
387
if max_age and not should_cull :
386
388
# only check created if max_age is specified
387
389
# so that we can still be compatible with jupyterhub 0.8
388
390
# which doesn't define the 'started' field
389
391
if age is not None and age .total_seconds () >= max_age :
390
- app_log .info (
392
+ logger .info (
391
393
f"Culling user { user ['name' ]} "
392
394
f"(age: { format_td (age )} , inactive for { format_td (inactive )} )"
393
395
)
394
396
should_cull = True
395
397
396
398
if not should_cull :
397
- app_log .debug (
399
+ logger .debug (
398
400
f"Not culling user { user ['name' ]} "
399
401
f"(created: { format_td (age )} , last active: { format_td (inactive )} )"
400
402
)
@@ -424,7 +426,7 @@ async def handle_user(user):
424
426
async for user in fetch_paginated (req ):
425
427
n_idle += 1
426
428
futures .append ((user ["name" ], handle_user (user )))
427
- app_log .debug (f"Got { n_idle } users with inactive servers" )
429
+ logger .debug (f"Got { n_idle } users with inactive servers" )
428
430
429
431
if state_filter :
430
432
params ["state" ] = "ready"
@@ -440,18 +442,18 @@ async def handle_user(user):
440
442
futures .append ((user ["name" ], handle_user (user )))
441
443
442
444
if state_filter :
443
- app_log .debug (f"Got { n_users } users with ready servers" )
445
+ logger .debug (f"Got { n_users } users with ready servers" )
444
446
else :
445
- app_log .debug (f"Got { n_users } users" )
447
+ logger .debug (f"Got { n_users } users" )
446
448
447
449
for name , f in futures :
448
450
try :
449
451
result = await f
450
452
except Exception :
451
- app_log .exception (f"Error processing { name } " )
453
+ logger .exception (f"Error processing { name } " )
452
454
else :
453
455
if result :
454
- app_log .debug ("Finished culling %s" , name )
456
+ logger .debug ("Finished culling %s" , name )
455
457
456
458
457
459
class IdleCuller (Application ):
@@ -577,6 +579,22 @@ def _default_cull_every(self):
577
579
config = True ,
578
580
)
579
581
582
+ _log_formatter_cls = LogFormatter
583
+
584
+ @default ('log_level' )
585
+ def _log_level_default (self ):
586
+ return logging .INFO
587
+
588
+ @default ('log_datefmt' )
589
+ def _log_datefmt_default (self ):
590
+ """Exclude date from default date format"""
591
+ return "%Y-%m-%d %H:%M:%S"
592
+
593
+ @default ('log_format' )
594
+ def _log_format_default (self ):
595
+ """override default log format to include time"""
596
+ return "%(color)s[%(levelname)1.1s %(asctime)s.%(msecs).03d %(name)s %(module)s:%(lineno)d]%(end_color)s %(message)s"
597
+
580
598
max_age = Int (
581
599
0 ,
582
600
help = dedent (
@@ -671,7 +689,7 @@ def start(self):
671
689
try :
672
690
AsyncHTTPClient .configure ("tornado.curl_httpclient.CurlAsyncHTTPClient" )
673
691
except ImportError as e :
674
- app_log .warning (
692
+ self . log .warning (
675
693
f"Could not load pycurl: { e } \n "
676
694
"pycurl is recommended if you have a large number of users."
677
695
)
@@ -682,6 +700,7 @@ def start(self):
682
700
url = self .url ,
683
701
api_token = api_token ,
684
702
inactive_limit = self .timeout ,
703
+ logger = self .log ,
685
704
cull_users = self .cull_users ,
686
705
remove_named_servers = self .remove_named_servers ,
687
706
max_age = self .max_age ,
0 commit comments