Container Logs logo

Docker & Portainer New

Connect Container Logs to Excel, Sheets and AI

Docker writes a log for every container — query all of them at once. The json-file logs the daemon writes, the docker logs exports you save by hand, and Portainer’s own server log. Point Query Streams at the folder and every line becomes a row — carrying the container’s real name, its image and its Compose service, instead of a directory full of 64-character ids.

1connection
0inbound ports
read-onlyenforced
3 container log formats Docker · Portainer Named by container, image and service Read-only · nothing installed in the container
The big idea

One folder of container logs becomes one table.

Query Streams reads the files the daemon already writes, pins the format, and gives you typed columns with the container named beside every row. No sidecar, no shipping pipeline, no index to maintain — and a line the parser cannot read is kept as a row with a reason rather than silently dropped.

Log folderon the host
a3f9c1…-json.logDocker
Dockershopfront-stack_api.logexport
portainer.logPortainer

Rotations, compressed archives and re-read files — handled

noisy-containers.sqlSQL
SELECT app,
       compose_service,
       count(*) AS lines
FROM   fileset.events
WHERE  stream = 'stderr'
GROUP BY app, compose_service
ORDER BY lines DESC
Resultstderr by container
appservicelines
shopfront-apiapi1,284
shopfront-workerworker417
shopfront-webweb62
shopfront-cachecache3

Join it to your application database in the same query

Files the daemon already writes, now a table you can report on

Sources

Two tools, three formats, no sidecar

Each card names the exact files Query Streams reads and where they live. You do not change your logging driver to make this work — json-file is the Docker default, and the default is the supported one.

Supported container logs

3 formats · 2 tools
Dockerjson-file logging driver
  • One {"log","stream","time"} envelope per line, written by the daemon
  • The container name comes from Docker’s own config.v2.json beside the log
  • Image and Compose project/service ride along as columns
  • Usually /var/lib/docker/containers
  • Appends; numbered rotations and .gz followed
Dockerdocker logs export
  • Files you saved with docker logs --timestamps
  • An RFC 3339 nanosecond stamp, then the container’s own line
  • Container banner lines are skipped by declaration, not by luck
  • Any folder you keep the exports in
  • One file per service; the name comes from the file stem
PortainerServer log, zerolog console
  • portainer.log or the container’s own output
  • ANSI colour codes stripped before parsing, so a level is a level
  • The key=value tail becomes columns; the remainder lands in data
  • The embedded tunnel server’s differently shaped lines are read too
  • Minute-resolution stamps, read as UTC
More comingthese are the start, not the ceiling

New container log formats are added with each Agent release. Because Agents keep themselves up to date, a format added later shows up in your connector without anyone visiting a server.

All three parsers were built and tested against real container logs — json-file logs, large docker logs exports off a Swarm host, and Portainer CE 2.39.1’s server log. One thing to know up front: on Docker Desktop for Windows and Mac the json-file logs live inside its virtual machine, where an Agent running as a service cannot reach them. Export them with docker logs --timestamps and use the second profile.
Anywhere it runs

Containers in every region, logs read where they sit

Containers rarely all run in one place. A host in the office, a Swarm cluster in one cloud region, a single box in another country. Each location runs a Network Agent that reads the log files where they already are and dials out to Query Streams. As far as your firewall is concerned that is an ordinary outbound connection — there is nothing to open and no VPN to build.

Head office A Docker host on local disk, json-file logs under the daemon’s folder dials out
eu-west A Swarm cluster, one docker logs export per service dials out
us-east Portainer managing a handful of hosts, its own server log alongside dials out

Three outbound connections, one place to query them — no inbound port, no VPN, no firewall change

One Agent per location

An Agent covers a whole site rather than a single folder: each log format you want there becomes its own File Set connector on that same Agent. One Agent per location is the normal shape. The free tier runs a single Agent and higher tiers run more.

1 site = 1 Agent = many connectors

Nothing to open

The Agent makes one encrypted outbound connection, and the request out and the rows back both travel over it. No inbound port, no VPN, no firewall change to go and ask for — and credentials stay inside your network.

one connection, both directions

One query across sites

Every source in a federated query names its own Agent, so a single statement can read a log in one country and a log in another and hand back one result — or join either of them to a database. Included on Business and Enterprise.

2 countries → 1 result set

They keep themselves current

Agents update themselves, so a container log format added in a later release reaches every site without anyone logging into a server to install it. That is what makes the wall above a starting point rather than a fixed list.

new formats arrive with the Agent

The hard part

Names, clocks and colour codes

A container log is not a text file with a date on it. Three things routinely go wrong when you try to treat one as data, and each is handled here by declaration rather than by guesswork.

Names, not hex ids

Docker names each log folder after the container id, which makes a grouped query unreadable. Query Streams reads the record Docker writes beside the log and takes the real name, keeping the id as its own column.

a3f9c1…-json.log → app = shopfront-api

The daemon’s clock

A container that prints its own timestamp in epoch seconds lands in 1970 if you believe it. The event time is the daemon’s capture time; the container’s own stamp is kept as a column instead of being trusted.

envelope time → event_time

Colour codes in the file

Console loggers write terminal escape sequences straight into the log. They are stripped before parsing, so a level reads as a level rather than as a level wrapped in escape characters — and the original bytes are left untouched.

escape codes removed → level = INF

Nothing counted twice

Each file is fingerprinted by its first 4 KB, so a rotated file is recognised as the same file rather than read again from the top. A rolling duplicate window catches the overlap when a writer replays its tail.

4 KB prefix hash · 20,000-record window

The schema

The columns you actually get

Not a blob of text with a timestamp beside it. Each format is parsed into typed columns you can filter, group and aggregate on directly — and where a container logs structured JSON, its own fields are unwrapped into columns too.

Docker logo
json-file container logDocker daemon
streamlevelloggermessagehostservicedata
Docker logo
docker logs exportDocker CLI
message
Portainer logo
zerolog console logPortainer
levelcallermessageerrordata

Every row also carries event_time, the raw stamp it was read from, and the file it came from. Each format adds its identity column — app for json-file and Portainer, container for an export — and json-file rows additionally carry image, compose_project, compose_service and container_id. The single message column on an export is deliberate: the container’s own line is kept whole rather than guessed at.

One connection, every surface

Where your container log data can go

Log files are not a dead end. Connect the folder once and the same read-only connection feeds every surface Query Streams supports — no second setup, no second copy of the data, and no difference in treatment from a database connector.

Supported

Container logs to Excel

Microsoft Excel · Excel add-in

Pull live container log results straight into a worksheet and refresh them on demand — desktop Excel, Excel Online, Microsoft 365.

How Excel works
Supported

Container logs to Google Sheets

Sheets add-on

Run a saved container log query from the sidebar and drop the rows into the sheet. Shared collaborators can refresh it themselves.

How Google Sheets works
Supported

Container logs MCP server

Claude, Cursor and MCP clients · MCP server

Give an AI assistant read-only access to container log with the schema it needs to write correct SQL — no credentials in the chat.

How MCP works
Supported

Container logs REST API

HTTP endpoint

Publish a container log query as an authenticated JSON endpoint any application can call, with an OpenAPI 3.1 spec and ready-made Postman, Insomnia and Hoppscotch collections. No database port is opened.

How REST API works
Supported

Container logs to Airtable

Automation platform

Sync container log rows into an Airtable base on a schedule, or fetch them inside an Airtable automation script.

How Airtable works
Supported

Container logs to Baserow

Automation platform

Feed a Baserow table from container log over the REST endpoint — self-hosted or Baserow cloud.

How Baserow works
Supported

Container logs to SeaTable

Automation platform

Keep a SeaTable base current with container log data without exporting a file or exposing the database.

How SeaTable works
Supported

Container logs to Smartsheet

Automation platform

Push container log results into a Smartsheet grid so plans and reports read from the source system, not last week's export.

How Smartsheet works
Supported

Container logs to Anvil

Anvil Works · App platform

Back an Anvil Python app with container log through the REST endpoint instead of embedding database credentials in the app.

How Anvil works
Supported

Container logs to Power BI

Power Query M

Paste the generated Power Query M into the Power BI Advanced Editor and the report reads live container log results over HTTPS — no ODBC driver, no database port opened.

How Power BI works
Supported

Container logs alerts and reports

Slack · Discord · Email · Webhook

Put a container log query on a schedule and have the rows delivered to Slack, Discord, email or a signed webhook — or hold the message until a row count, threshold or percentage change crosses the line you set.

How alerts and reports work
The alerts card is the one worth a second look for containers: a scheduled query against your error count needs nobody watching a terminal, so a restart loop reaches Slack on its own. Log-specific step-by-step guides are not written yet — the linked pages cover how each surface works.

What this deliberately does not do

It is not a live tail. Query Streams reads the files as they are on disk when you run the query, so a restart loop shows up as rows rather than as a stream you watch. If you need sub-second alerting on a line as it is printed, a log pipeline is the right tool.

A container’s output is freeform, and that has a consequence worth saying plainly: whatever your application prints is what lands in the table. If it logs a token or a customer email, that string is already in the log file and it will be in the column too. There is no header map to exclude the way there is for a web server log — treat the resulting table with exactly the care you would treat the file.

Two smaller limits, named rather than hidden. On a docker logs export the container’s own line is kept whole in one message column; re-parsing that inner line by its own format is not built yet. And the optional attributes Docker can attach with a logging tag are recognised by shape only — no container in the captured corpus used them, so that path is declared, not proven.

The rest follows the usual design. The Agent connects outbound and reads files; nothing is installed in the container, nothing is written back, the Docker socket and API are never touched, and access is read-only. The retention field starts at 30 days for this preset and is yours to change.

How it works

Three steps, and nothing gets installed in a container.

01

Point at the log folder

Give a Query Streams Agent read access to where the logs already land — /var/lib/docker/containers on a Docker host, a folder of saved exports, or a share you collect them onto.

02

It recognises the format

Query Streams identifies the format from the content of the lines, not the filename — so a renamed copy still reads, and a file that does not match the pinned format is parked with a reason instead of corrupting the table.

03

Query it, or join it

Run SQL from the portal, land it live in Microsoft Excel or Google Sheets, or use a federated query to join container logs to the users and orders in your application database — in one statement.

Container log FAQ

Do I have to change my Docker logging driver?

No. json-file is Docker’s default driver, and it is the supported one. If you have never configured logging, the files are already there and already in the right shape.

If you have moved to a different driver, the docker logs --timestamps export is the route in: it works whatever the driver, because the CLI reads through the daemon.

How does it know the container’s name and not just its id?

Docker writes its own record of a container in the same folder as the log. Query Streams reads the name out of that file and uses it as the identity column, with the image and the Compose project and service kept as columns beside it. The 64-character folder id is retained as container_id, so nothing is lost.

If that record is missing, unreadable or does not carry a name, the folder name is used instead and the sweep counts how often that happened — so a fallback shows up as a number you can look at, not as a silent substitution.

I am on Docker Desktop for Windows or Mac. Does this work?

Not against the json-file logs directly. Docker Desktop keeps those inside its own virtual machine, and an Agent running as a service on the host cannot see into it.

The way through is the export profile: docker logs --timestamps <container> redirected to a file, in any folder the Agent can read. That is a supported format in its own right, not a workaround — it is the same route people already use to keep a copy of a log after a container is gone.

Is this real-time?

It is on-demand rather than streaming. The files are read as they stand when the query runs, so new lines appear as your containers write them and the file is picked up — not on a fixed nightly schedule, but not a live tail either.

For asking a question of the last few weeks across every container and getting the answer into a spreadsheet, that is the useful shape and considerably less machinery than a log stack.

What about Kubernetes or Podman?

Neither has a profile of its own today, and we would rather say so than imply coverage. What does apply is the export format: the profile is keyed to the exact shape docker logs --timestamps writes — an RFC 3339 nanosecond stamp, a space, then the container’s own line, one file per service. Tooling that writes that same shape reads with it.

The reliable way to find out for your own files is Test connection, which reports what it recognised before you commit to anything.

My containers log JSON. Do I get their fields as columns?

Yes, for json-file logs. The daemon’s envelope is unwrapped and the container’s own JSON inside it is flattened into columns — level, logger, message, host and service where they are present. Anything that does not map to a named column is kept in a data column rather than discarded, so an unusual field is still queryable.

The one thing not taken from the container’s JSON is the timestamp. That comes from the daemon, because an application’s own stamp can be in any format and getting it wrong silently misplaces the row in time.

What about a line the parser cannot read?

It is kept, not dropped. A line that does not match the pinned format is retained as a raw row with a reason attached, so a malformed entry or an unexpected format change shows up as something you can see and query rather than as a silent gap in your numbers.

Your containers are already talking

Connect a log folder and run your first query against it in a few minutes. Free tier, no credit card, nothing installed in a container.

Read-only · outbound connection only · your logs stay on your host