Skip to content

Logging and debugging

tcely edited this page Aug 1, 2026 · 8 revisions

Logging and debugging

TubeSync outputs useful logs, errors and debugging information to the console.

Tip

Even more detailed logs are displayed on the console when the environment variable TUBESYNC_DEBUG is set to True.

Important

TubeSync is only a stand-in for your actual container name or identifier. You should adjust this value, as needed, for your environment.

You can view the console logs with:

$ docker logs --follow TubeSync

To include logs with an issue report, please extract a file and attach it to the issue. The command below creates the TubeSync.logs.txt file with the logs from the console of the TubeSync container instance:

docker logs -t TubeSync > TubeSync.logs.txt 2>&1

Tip

Log files are highly compressible. You can place any combination of files into a .zip archive to save space and make them easier to attach to an issue.

hat-syslog

Important

Only available with v0.18.0 and newer versions.

Access to the historical and live logs is available from a web browser at: http://HOSTNAME_OR_IP:4848/web-logs/index.html

Note

Whichever value the TUBESYNC_DEBUG environment variable was set to, the more detailed logs will remain available from the /web-logs/index.html page.

It is possible to copy the logs database (stored at /config/state/hat/syslog.db) or the web logs (stored at /run/app/log/messages) from a container using the docker container cp command.

You can also create flat files from your current syslog database using the tools included inside the container (as of 7f50278):

docker exec -it TubeSync /usr/bin/env \
    bash -c 'python3 /app/hat-syslog_tool.py --export --out "$(mktemp -d /downloads/cache/live-logs-XXXXXXX)/exported" /config/state/hat/syslog.db'

After that export has completed, you can access the files any way that you prefer.

Viewing those logs can be done with less:

docker exec -t TubeSync /usr/bin/env \
    bash -c 'cat /downloads/cache/live-logs-*/exported/messages.log' | \
    less

These exported logs typically include more information than is shown in the console logs.

Cleanup of old files

Taken from this comment: https://github.com/meeb/tubesync/issues/1541#issuecomment-5148630766

You can safely remove any file beginning with syslog.db that has . and a number at the end.

For shell, the glob would be: syslog.db.[0-9]*

We are using the default rotation feature, so you may safely remove (or move to an archive) any rotated database files.

--db-enable-archive

https://github.com/meeb/tubesync/blob/59c45d1d8c9c1e85b548466d4709d7851194670a/config/root/etc/s6-overlay/s6-rc.d/hat-syslog-server/run#L14-L15

    hat-syslog-server --log-level 'INFO' \
        --db-enable-archive --db-path "${DATABASE}"

All incoming syslog messages are stored in single sqlite database. Maximum number of syslog messages stored in this database can be configured by configuration parameter db_high_size (value 0 represents unlimited number of messages). Once number of messages exceed configured limit, database cleanup procedure is triggered. During cleanup procedure, oldest messages are removed from database until number of messages reaches configuration parameter db_low_size when cleanup procedure stops. Prior to message deletion, if configuration parameter db_enable_archive is set, new database with unique file name is created and all messages scheduled for removal are inserted into newly created database. Archive database has got same structure as original database and can be used in place of original database for accessing archived syslog messages.

https://github.com/hat-open/hat-syslog/blob/9ab54625aa0987106ebda10c6d1acedf840d039b/src_py/hat/syslog/server/backend.py#L186-L232

    async def _db_cleanup(self):
        first_id = self._last_id - self._low_size + 1
        if first_id > self._last_id:
            first_id = None
        if first_id <= self._first_id:
            return


        if self._enable_archive:
            mlog.debug("archiving database entries...")
            await self._archive_db(first_id)


        await self._db.delete(first_id)
        self._first_id = first_id
        if self._first_id is None:
            self._last_id = None


        mlog.debug("backend state changed (first_id: %s; last_id: %s)",
                   self._first_id, self._last_id)
        self._change_cbs.notify([])


    async def _archive_db(self, first_id):
        archive_path = await self._async_group.spawn(
            self._executor, _ext_get_new_archive_path, self._path)
        archive = await database.create_database(
            archive_path, self._disable_journal)
        try:
            entries = await self._db.query(common.Filter(
                last_id=first_id - 1 if first_id is not None else None))
            await archive.add_entries(entries)
        finally:
            await aio.uncancellable(archive.async_close())




def _ext_get_new_archive_path(db_path):
    last_index = 0


    for i in db_path.parent.glob(db_path.name + '.*'):
        with contextlib.suppress(ValueError):
            index = int(i.name.split('.')[-1])
            if index > last_index:
                last_index = index


    for i in itertools.count(last_index + 1):
        new_path = db_path.parent / f"{db_path.name}.{i}"
        if new_path.exists():
            continue
        return new_path

Clone this wiki locally