Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
3eeb695
update wrapmain() to reset logging.
vEpiphyte Jan 26, 2026
c27d66d
updated base.main() to reset logging.
vEpiphyte Jan 26, 2026
f6bd3a0
FIXME - There is a issue with logging behavior and and messages logge…
vEpiphyte Jan 26, 2026
fdc756f
inline a few changes; add some comments for myself
vEpiphyte Jan 26, 2026
4481538
Logging notes
vEpiphyte Jan 26, 2026
3f792af
Add branch build for containers
vEpiphyte Jan 26, 2026
b68f15b
Fix whitespace
vEpiphyte Jan 26, 2026
5a713cf
Fix branch build configuration
vEpiphyte Jan 27, 2026
f8c9bd8
Capture sorc=None as a sentinel <none> so we always have a string val…
vEpiphyte Jan 27, 2026
d108312
Add a logging.shutdown() method which ensures that the logging queue …
vEpiphyte Jan 27, 2026
bbf25ed
Try some different values for the embedded ahasvcname field
vEpiphyte Jan 28, 2026
22d4ae9
Test with aha:service
vEpiphyte Jan 29, 2026
a9b125c
Fix datefmt issue in structlog test
vEpiphyte Jan 29, 2026
592e930
user is logged twice in _logStormQuery
vEpiphyte Jan 29, 2026
d1e2522
Merge branch 'visi-logging' into visi-logging-epiphyte-fb
vEpiphyte Jan 29, 2026
1cd9aee
Cleanup some of the direct extra= construction.
vEpiphyte Jan 29, 2026
a7374f1
Remove unused imports
vEpiphyte Jan 29, 2026
30fb980
Additional extra cleanup
vEpiphyte Jan 29, 2026
ba94eae
We have deferred imports until we get PEP-810
vEpiphyte Jan 29, 2026
79e248b
Fix lint errors
vEpiphyte Jan 29, 2026
a3cea82
ahs:service to ahaservice
vEpiphyte Jan 29, 2026
9e87d78
Fix ref error
vEpiphyte Jan 29, 2026
a4625cf
Fix test_model_invalid_comp_types
vEpiphyte Jan 29, 2026
d74d237
Remove stray_base. It was only used for testing.
vEpiphyte Jan 29, 2026
e8612f2
Restore the double logging for username and user atm
vEpiphyte Jan 29, 2026
c546e1d
Remove super() comment
vEpiphyte Jan 29, 2026
46580a5
Mark structlog.JsonFormatter as deprecated.
vEpiphyte Jan 29, 2026
30d2d2d
Fix lint error
vEpiphyte Jan 29, 2026
2f36165
Do not log the telepath session id
vEpiphyte Jan 30, 2026
78b380c
Test - Give preference to runtime user scope -> then explicit user ->…
vEpiphyte Jan 30, 2026
50b092d
Fix lint error
vEpiphyte Jan 30, 2026
42e28a1
revert runt change
vEpiphyte Jan 30, 2026
e2744ec
Merge branch 'visi-logging' into visi-logging-epiphyte-fb
vEpiphyte Feb 2, 2026
141eda6
docstrings for expect()
vEpiphyte Feb 3, 2026
d7fb94f
Fix tests
vEpiphyte Feb 3, 2026
72ac941
Run a CI suite with pytest.ini
vEpiphyte Feb 3, 2026
aae8d2d
Revert "Run a CI suite with pytest.ini"
vEpiphyte Feb 3, 2026
b1e5416
Clarify a comment
vEpiphyte Feb 3, 2026
d8d4fa9
log the api name in adminapi() message
vEpiphyte Feb 3, 2026
ea08d8d
Remove obe import
vEpiphyte Feb 3, 2026
b73c352
Thought shall not make synapse/common have import side effects.
vEpiphyte Feb 3, 2026
a30fbfc
add a few test assertions along logging
vEpiphyte Feb 4, 2026
5778dc2
Merge branch 'visi-logging' into visi-logging-epiphyte-fb
vEpiphyte Feb 5, 2026
b3902a6
Revert "Thought shall not make synapse/common have import side effects."
vEpiphyte Feb 5, 2026
b949b61
We're now free to import synapse.lib.logging much easiser.
vEpiphyte Feb 5, 2026
d52510d
Merge branch 'visi-logging' into visi-logging-epiphyte-fb
vEpiphyte Feb 5, 2026
705ba8e
Queue import is no longer circular
vEpiphyte Feb 5, 2026
c2ed244
Revert "Queue import is no longer circular"
vEpiphyte Feb 5, 2026
d74926b
Merge branch 'visi-logging' into visi-logging-epiphyte-fb
vEpiphyte Feb 12, 2026
37fd62f
Refactor logging tests to better isolate pieces of functionality, add…
vEpiphyte Feb 12, 2026
292df91
Add tests for shutdown behavor for a running pumptask
vEpiphyte Feb 13, 2026
47ed379
Update docs
vEpiphyte Feb 13, 2026
8e5095e
Coverage for error handling inside of _pumpLogStream
vEpiphyte Feb 13, 2026
c8fd04b
colon
vEpiphyte Feb 13, 2026
f6a80ad
no cover
vEpiphyte Feb 15, 2026
c8c2350
no cover
vEpiphyte Feb 15, 2026
1f647d4
Add seatbelts around SYNDEV_LOG envars, add test to show the message …
vEpiphyte Feb 16, 2026
8e0e96c
Ensure that exceptionGroup is handled.
vEpiphyte Feb 17, 2026
dbd692a
update docs
vEpiphyte Feb 17, 2026
74c1d83
Changelog for deprecation.
vEpiphyte Feb 17, 2026
62c2063
ahaservice to service
vEpiphyte Mar 1, 2026
1d2bfd2
Don't allow +LOG_PUMP_TASK_TIMEOUT and +LOG_QUEUE_SIZES to be tunable
vEpiphyte Mar 1, 2026
77fd839
Merge branch 'visi-logging' into visi-logging-epiphyte-fb
vEpiphyte Mar 5, 2026
b40908c
Ensure that user scope is tracked more consistently.
vEpiphyte Mar 5, 2026
2db55de
Clone the current scope onto the nexus applytastk
vEpiphyte Mar 5, 2026
28d79a3
Update a getLogExtra call
vEpiphyte Mar 6, 2026
d649b8c
Remove unused import
vEpiphyte Mar 6, 2026
b0fc9ce
Set the user scope when starting dmon's right away
vEpiphyte Mar 6, 2026
aef4b6d
http auth failures go to target_user / target_username"
vEpiphyte Mar 6, 2026
e9dd62a
Revert "Set the user scope when starting dmon's right away"
vEpiphyte Mar 6, 2026
9c7a00e
Tidy
vEpiphyte Mar 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,7 @@ workflows:
branches:
only:
- master
- visi-logging-epiphyte-fb

- build_docker_tag:
requires:
Expand Down
7 changes: 7 additions & 0 deletions changes/7ef2889edb0c2bfb214207b97f0394db.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
desc: Deprecated ``synapse.lib.structlog.JsonFormatter``. It will be removed after
April 1st, 2026.
desc:literal: false
prs: []
type: deprecation
...
81 changes: 56 additions & 25 deletions docs/synapse/devopsguide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -346,46 +346,77 @@ These structured logs are designed to be easy to ingest into third party log col
message, level, time, and metadata about where the log message came from::

{
"message": "log level set to INFO",
"ahaservice": "00.cortex.synapse",
"message": "Executing storm query {[inet:asn=1234]} as [someUsername]",
"logger": {
"name": "synapse.lib.cell",
"name": "synapse.storm",
"func": "_logStormQuery",
"process": "MainProcess",
"filename": "common.py",
"func": "setlogging"
"thread": "MainThread"
},
"level": "INFO",
"time": "2021-06-28 15:47:54,825"
"time": "2026-02-13 10:38:24,545",
"user": "a8b94560fd9b6e1e38245a6f7d659106",
"username": "root",
"params": { # Param
"mode": "storm",
"view": "715a5c9ae37e4045795ea3f3cabb44fb",
"text": "[inet:asn=1234]",
"username": "someUsername",
"user": "e3532bc88fa66afb592e6a1474a98675",
"hash": "ef94e89eb3bc309a40242876f6c5f296"
}
}

The ``user`` and ``username`` fields at the top level of a log correspond to the currently active / authorized API user
which has caused a log event to occur. These may differ from the user associated with the event. That information would
normally be embedded in the ``params`` key. The ``ahaservice`` key, if present, indicates the service that is
associated with the log message.

When exceptions are logged with structured logging, we capture additional information about the exception, including the
entire traceback. In the event that the error is a Synapse Err class, we also capture additional metadata which was
attached to the error. In the following example, we also have the query text, username and user iden available in the
log message pretty-printed log message::
traceback as structured data. In the event that the error is a Synapse Err class, we also capture additional metadata
which was attached to the error. In the following example, we also have the query text, username and user iden available
in the pretty-printed log message. The ``key=valu`` data that was raised by the user is also included in the
``error.info.`` dictionary. Cause and Context information may be present, corresponding to the Python ``__cause__`` and
``__context__`` attributes on Exception classes.::

{
"message": "Error during storm execution for { || }",
"ahaservice": "00.cortex.synapse",
"message": "Error during storm execution for { $lib.raise(Newp, 'ruh roh', key=valu) }",
"logger": {
"name": "synapse.lib.view",
"func": "runStorm",
"process": "MainProcess",
"filename": "view.py",
"func": "runStorm"
"thread": "MainThread"
},
"level": "ERROR",
"time": "2021-06-28 15:49:34,401",
"err": {
"efile": "coro.py",
"eline": 233,
"esrc": "return await asyncio.get_running_loop().run_in_executor(forkpool, _runtodo, todo)",
"ename": "forked",
"at": 1,
"text": "||",
"mesg": "No terminal defined for '|' at line 1 col 2. Expecting one of: #, $, (, *, + or -, -(, -+>, -->, ->, :, <(, <+-, <-, <--, [, break, command name, continue, fini, for, function, if, init, property name, return, switch, while, whitespace or comment, yield, {",
"etb": ".... long traceback ...",
"errname": "BadSyntax"
},
"text": "||",
"time": "2026-02-13 11:24:06,853",
"user": "e889d295b19fd5c12f861cd6fe564aa8",
"username": "root",
"user": "3189065f95d3ab0a6904e604260c0be2"
"error": {
"code": "StormRaise",
"traceback": [
... list of traceback frames ...
[
"/home/epiphyte/PycharmProjects/synapse/synapse/lib/stormtypes.py",
1751,
"_raise",
"raise s_exc.StormRaise(**info)"
]
],
"info": {
"key": "valu",
"errname": "Newp",
... additional error information ...
}
},
"mesg": "ruh roh"
},
"params": {
"text": "$lib.raise(Newp, 'ruh roh', key=valu)",
"user": "e3532bc88fa66afb592e6a1474a98675",
"username": "someUsername"
}
}

Custom date formatting strings can also be provided by setting the ``SYN_LOG_DATEFORMAT`` string. This is expected to be a
Expand Down
1 change: 0 additions & 1 deletion synapse/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
import synapse.lib.const as s_const
import synapse.lib.logging as s_logging
import synapse.lib.msgpack as s_msgpack
import synapse.lib.structlog as s_structlog

import synapse.vendor.cpython.lib.ipaddress as ipaddress
import synapse.vendor.cpython.lib.http.cookies as v_cookies
Expand Down
15 changes: 5 additions & 10 deletions synapse/cortex.py
Original file line number Diff line number Diff line change
Expand Up @@ -1058,15 +1058,15 @@ async def initServiceStorage(self):

mesg = f'User {useriden} ({user.name}) has a rule on the "cortex" authgate. This authgate is not used ' \
f'for permission checks and will be removed in Synapse v3.0.0.'
logger.warning(mesg, extra=self.getLogExtra(user=useriden, username=user.name))
logger.warning(mesg)
for roleiden in ag.gateroles.keys():
role = self.auth.role(roleiden)
if role is None:
continue

mesg = f'Role {roleiden} ({role.name}) has a rule on the "cortex" authgate. This authgate is not used ' \
f'for permission checks and will be removed in Synapse v3.0.0.'
logger.warning(mesg, extra=self.getLogExtra(role=roleiden, rolename=role.name))
logger.warning(mesg)

self._initVaults()

Expand Down Expand Up @@ -6449,8 +6449,7 @@ async def _getStormEval(self, text):
try:
astvalu = copy.deepcopy(await s_parser.evalcache.aget(text))
except s_exc.FatalErr: # pragma: no cover
extra = self.getLogExtra(text=text)
logger.exception(f'Fatal error while parsing [{text}]', extra=extra)
logger.exception(f'Fatal error while parsing [{text}]', extra=self.getLogExtra(text=text))
await self.fini()
raise
astvalu.init(self)
Expand All @@ -6460,8 +6459,7 @@ async def _getStormQuery(self, args):
try:
query = copy.deepcopy(await s_parser.querycache.aget(args))
except s_exc.FatalErr: # pragma: no cover
extra = self.getLogExtra(text=args[0])
logger.exception(f'Fatal error while parsing [{args}]', extra=extra)
logger.exception(f'Fatal error while parsing [{args}]', extra=self.getLogExtra(text=args[0]))
await self.fini()
raise
query.init(self)
Expand Down Expand Up @@ -6511,12 +6509,9 @@ def _logStormQuery(self, text, user, info=None):
if info is None:
info = {}
info['text'] = text
info['username'] = user.name
info['user'] = user.iden
info['hash'] = s_storm.queryhash(text)
extra = s_logging.getLogExtra(**info)
stormlogger.log(self.stormloglvl, 'Executing storm query {%s} as [%s]', text, user.name,
extra=extra)
extra=self.getLogExtra(**info))

async def getNodeByNdef(self, ndef, view=None):
'''
Expand Down
9 changes: 4 additions & 5 deletions synapse/datamodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import synapse.lib.types as s_types
import synapse.lib.dyndeps as s_dyndeps
import synapse.lib.grammar as s_grammar
import synapse.lib.logging as s_logging
import synapse.lib.msgpack as s_msgpack

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -948,7 +949,6 @@ def addType(self, typename, basename, typeopts, typeinfo, checks=True):
def _checkTypeDef(self, typ):
if 'comp' in typ.info.get('bases', ()):
for fname, ftypename in typ.opts.get('fields', ()):
extra = {'synapse': {'type': typ.name, 'field': fname}}

if isinstance(ftypename, (list, tuple)):
ftypename = ftypename[0]
Expand All @@ -957,7 +957,7 @@ def _checkTypeDef(self, typ):
ftype = typ.tcache[fname]
except s_exc.BadTypeDef:
mesg = f'The {typ.name} field {fname} is declared as a type ({ftypename}) that does not exist.'
logger.warning(mesg, extra=extra)
logger.warning(mesg, s_logging.getLogExtra(type=typ.name, field=fname))
continue

# We're only interested in extended model comp types
Expand All @@ -966,12 +966,11 @@ def _checkTypeDef(self, typ):

if ftype.ismutable:
mesg = f'Comp types with mutable fields ({typ.name}:{fname}) are deprecated and will be removed in 3.0.0.'
logger.warning(mesg, extra=extra)
logger.warning(mesg, s_logging.getLogExtra(type=typ.name, field=fname))

if ftype.deprecated:
mesg = f'The type {typ.name} field {fname} uses a deprecated type {ftype.name}.'
extra['synapse']['field:type'] = ftype.name
logger.warning(mesg, extra=extra)
logger.warning(mesg, s_logging.getLogExtra(type=typ.name, field=fname, field_type=ftype.name))

def addForm(self, formname, forminfo, propdefs, checks=True):

Expand Down
34 changes: 15 additions & 19 deletions synapse/lib/agenda.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import synapse.lib.base as s_base
import synapse.lib.coro as s_coro
import synapse.lib.scope as s_scope
import synapse.lib.logging as s_logging

# Agenda: manages running one-shot and periodic tasks in the future ("appointments")
Expand Down Expand Up @@ -832,22 +833,18 @@ async def runloop(self):
if appt.isrunning: # pragma: no cover
mesg = f'Appointment {appt.iden} {appt.name} is still running from previous time when scheduled' \
f' to run. Skipping.'
logger.warning(mesg,
extra={'synapse': {'iden': appt.iden, 'name': appt.name}})
logger.warning(mesg, extra=self.core.getLogExtra(iden=appt.iden, name=appt.name))
else:
try:
await self._execute(appt)
except Exception as e:
extra = {'iden': appt.iden, 'name': appt.name, 'user': appt.creator, 'view': appt.view}
user = self.core.auth.user(appt.creator)
if user is not None:
extra['username'] = user.name
extra = {'iden': appt.iden, 'name': appt.name, 'view': appt.view}
if isinstance(e, s_exc.SynErr):
mesg = e.get('mesg', str(e))
else: # pragma: no cover
mesg = str(e)
logger.exception(f'Agenda error running appointment {appt.iden} {appt.name}: {mesg}',
extra={'synapse': extra})
extra=self.core.getLogExtra(**extra))
await self._markfailed(appt, f'error: {e}')

async def _execute(self, appt):
Expand All @@ -857,30 +854,29 @@ async def _execute(self, appt):
user = self.core.auth.user(appt.creator)
if user is None:
logger.warning(f'Unknown user {appt.creator} in stored appointment {appt.iden} {appt.name}',
extra={'synapse': {'iden': appt.iden, 'name': appt.name, 'user': appt.creator}})
extra=self.core.getLogExtra(iden=appt.iden, name=appt.name, user=appt.creator))
await self._markfailed(appt, 'unknown user')
return

locked = user.info.get('locked')
if locked:
logger.warning(f'Cron {appt.iden} {appt.name} failed because creator {user.name} is locked',
extra={'synapse': {'iden': appt.iden, 'name': appt.name, 'user': appt.creator,
'username': user.name}})
extra=self.core.getLogExtra(iden=appt.iden, name=appt.name))
await self._markfailed(appt, 'locked user')
return

view = self.core.getView(iden=appt.view, user=user)
if view is None:
logger.warning(f'Unknown view {appt.view} in stored appointment {appt.iden} {appt.name}',
extra={'synapse': {'iden': appt.iden, 'name': appt.name, 'user': appt.creator,
'username': user.name, 'view': appt.view}})
extra=self.core.getLogExtra(iden=appt.iden, name=appt.name, view=appt.view))
await self._markfailed(appt, 'unknown view')
return

info = {'iden': appt.iden, 'query': appt.query, 'view': view.iden}

coro = self._runJob(user, appt)
task = self.core.runActiveTask(coro)
task._syn_scope['user'] = user

appt.task = await self.core.boss.promotetask(task, f'Cron {appt.iden}', user, info=info)
async def fini():
Expand Down Expand Up @@ -912,8 +908,7 @@ async def _runJob(self, user, appt):
await self.core.addCronEdits(appt.iden, edits)

logger.info(f'Agenda executing for iden={appt.iden}, name={appt.name} user={user.name}, view={appt.view}, query={appt.query}',
extra={'synapse': {'iden': appt.iden, 'name': appt.name, 'user': user.iden, 'text': appt.query,
'username': user.name, 'view': appt.view}})
extra=self.core.getLogExtra(iden=appt.iden, name=appt.name, text=appt.query, view=appt.iden))
starttime = self._getNowTick()

success = False
Expand All @@ -940,8 +935,10 @@ async def _runJob(self, user, appt):

elif mesg[0] == 'warn' and loglevel <= logging.WARNING:
text = mesg[1].get('mesg', '<missing message>')
extra = self.core.getLogExtra(cron=appt.iden, **mesg[1])
logger.warning(f'Cron job {appt.iden} issued warning: {text}', extra=extra)
_params = mesg[1]
_params['iden'] = appt.iden
logger.warning(f'Cron job {appt.iden} issued warning: {text}',
extra=self.core.getLogExtra(**_params))

elif mesg[0] == 'err':
excname, errinfo = mesg[1]
Expand All @@ -957,7 +954,7 @@ async def _runJob(self, user, appt):
except Exception as e:
result = f'raised exception {e}'
logger.exception(f'Agenda job {appt.iden} {appt.name} raised exception',
extra={'synapse': {'iden': appt.iden, 'name': appt.name}}
extra=self.core.getLogExtra(iden=appt.iden, name=appt.name),
)
else:
success = True
Expand All @@ -981,8 +978,7 @@ async def _runJob(self, user, appt):
f'took {took:.3f}s'
if not self.core.isactive:
mesg = mesg + ' Agenda status will not be saved since the Cortex is no longer the leader.'
logger.info(mesg, extra={'synapse': {'iden': appt.iden, 'name': appt.name, 'user': user.iden,
'result': result, 'username': user.name, 'took': took}})
logger.info(mesg, extra=self.core.getLogExtra(iden=appt.iden, name=appt.name, result=result, took=took))
edits = {
'lastfinishtime': finishtime,
'isrunning': False,
Expand Down
19 changes: 8 additions & 11 deletions synapse/lib/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -1458,17 +1458,15 @@ async def tryPasswd(self, passwd, nexs=True, enforce_policy=True):
if expires >= s_common.now():
if await s_passwd.checkShadowV2(passwd=passwd, shadow=shadow):
await self.auth.setUserInfo(self.iden, 'onepass', None)
logger.debug(f'Used one time password for {self.name}',
extra={'synapse': {'user': self.iden, 'username': self.name}})
logger.debug(f'Used one time password for {self.name}')
return True
else:
# Backwards compatible password handling
expires, params, hashed = onepass
if expires >= s_common.now():
if s_common.guid((params, passwd)) == hashed:
await self.auth.setUserInfo(self.iden, 'onepass', None)
logger.debug(f'Used one time password for {self.name}',
extra={'synapse': {'user': self.iden, 'username': self.name}})
logger.debug(f'Used one time password for {self.name}')
return True

shadow = self.info.get('passwd')
Expand All @@ -1493,15 +1491,15 @@ async def tryPasswd(self, passwd, nexs=True, enforce_policy=True):

if self.iden == self.auth.rootuser.iden:
mesg = f'User {self.name} has exceeded the number of allowed password attempts ({valu + 1}),. Cannot lock {self.name} user.'
extra = {'synapse': {'target_user': self.iden, 'target_username': self.name, }}
logger.error(mesg, extra=extra)
logger.error(mesg, extra=self.auth.nexsroot.cell.getLogExtra(target_user=self.iden, target_username=self.name))
return False

await self.auth.nexsroot.cell.setUserLocked(self.iden, True)

mesg = f'User {self.name} has exceeded the number of allowed password attempts ({valu + 1}), locking their account.'
extra = {'synapse': {'target_user': self.iden, 'target_username': self.name, 'status': 'MODIFY'}}
logger.warning(mesg, extra=extra)
mesg = f'User {self.name} has exceeded the number of allowed password attempts ({valu + 1}), locked their account.'
logger.warning(mesg, extra=self.auth.nexsroot.cell.getLogExtra(target_user=self.iden,
target_username=self.name,
status='MODIFY'))

return False

Expand All @@ -1510,8 +1508,7 @@ async def tryPasswd(self, passwd, nexs=True, enforce_policy=True):
# Backwards compatible password handling
salt, hashed = shadow
if s_common.guid((salt, passwd)) == hashed:
logger.debug(f'Migrating password to shadowv2 format for user {self.name}',
extra={'synapse': {'user': self.iden, 'username': self.name}})
logger.debug(f'Migrating password to shadowv2 format for user {self.name}')
# Update user to new password hashing scheme. We cannot enforce policy
# when migrating an existing password.
await self.setPasswd(passwd=passwd, nexs=nexs, enforce_policy=False)
Expand Down
4 changes: 4 additions & 0 deletions synapse/lib/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import synapse.lib.coro as s_coro
import synapse.lib.scope as s_scope
import synapse.lib.logging as s_logging

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -599,6 +600,9 @@ async def main(self, timeout=BASE_MAIN_BG_TASK_TIMEOUT): # pragma: no cover
'''
await self.addSignalHandlers()
await self.waitfini()
# shutdown logging to allow it to drain any queued messages it has, swapping in a stream handler,
# and then cancellling the task so we do not have to await the pump task in bg tasks.
await s_logging.shutdown()
await s_coro.await_bg_tasks(timeout)

def waiter(self, count, *names, timeout=None):
Expand Down
Loading