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.
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.
a3f9c1…-json.logDocker
shopfront-stack_api.logexport
portainer.logPortainerRotations, compressed archives and re-read files — handled
SELECT app,
compose_service,
count(*) AS lines
FROM fileset.events
WHERE stream = 'stderr'
GROUP BY app, compose_service
ORDER BY lines DESCJoin it to your application database in the same query
Files the daemon already writes, now a table you can report on
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.jsonbeside the log - Image and Compose project/service ride along as columns
- Usually
/var/lib/docker/containers - Appends; numbered rotations and
.gzfollowed
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.logor the container’s own output- ANSI colour codes stripped before parsing, so a level is a level
- The
key=valuetail becomes columns; the remainder lands indata - The embedded tunnel server’s differently shaped lines are read too
- Minute-resolution stamps, read as UTC
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.
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.
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.
docker logs export per service
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
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 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.
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.
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.
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 worksContainer 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 worksContainer 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 worksContainer 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 worksContainer 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 worksContainer logs to Baserow
Automation platform
Feed a Baserow table from container log over the REST endpoint — self-hosted or Baserow cloud.
How Baserow worksContainer logs to SeaTable
Automation platform
Keep a SeaTable base current with container log data without exporting a file or exposing the database.
How SeaTable worksContainer 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 worksContainer 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 worksContainer 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 worksContainer 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 workWhat 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.
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.
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.
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












