# G-Log for administrators *What you need to know before you point it at a web server.* By **Garia.Net** · > This describes version 1.1.4. Everything below was run on a Windows 11 workstation with generated logs, not on > a production IIS server, and the installers were built and unpacked here but not installed on a server. Where > that matters, it says so. --- ## The problem, honestly stated A site is slow, or users see errors, and the answer is somewhere in a few gigabytes of IIS logs. Log Parser 2.2 can find it if you know the question and write the SQL, and it reads the whole file again for every question. A log platform can find it once the logs have been shipped there and you have a licence. G-Log reads the log once, all questions at the same time, and on a 10 GB log that takes under two seconds. The report starts with *is something wrong, where, since when, what changed and who notices*, and says honestly what the log cannot tell. --- ## Rights **Read access to the log folders is all it needs.** G-Log does not need to be an administrator, does not install anything and changes no IIS setting. | Logs | Default location | Who can read them | |---|---|---| | IIS sites | `%SystemDrive%\inetpub\logs\LogFiles\W3SVC` | set by the folder's ACL; check with `icacls` | | HTTP.sys errors | `%SystemRoot%\System32\LogFiles\HTTPERR` | on this Windows 11 machine (no IIS) the local *Users* group can read it; **on a server, check** | When a file cannot be read, the report says so in chapter 0 (the file, and why), and the other files are still analysed. **It never gets in the way of IIS.** Files are opened with read, write *and* delete sharing, so IIS keeps writing to today's log, rotates it and removes old ones while G-Log is reading. There is no memory mapping (a mapped file cannot be truncated or deleted) and no handle is kept between runs. Whether that holds up during a rollover on a busy server with antivirus active still has to be tested on a real server. **The report shows what the log contains:** client addresses, URLs with their query strings, the values of passwords, tokens and API keys found there, card numbers, and user names. `MASKSENSITIVE=1` at install time, or `--mask`, masks those in every report and MCP answer. --- ## Deploying One MSI, per machine, 64-bit, nothing else needed: the runtime is compiled in and the report page is a resource inside `glog.exe`. It goes out through GPO, Intune, SCCM or an RMM without a wrapper script: ```bat msiexec /i G-Log-1.1.4-x64.msi /qn ADDTOPATH=1 CONTEXTMENU=0 INSTALLFOLDER="D:\Tools\G-Log" ``` | Property | Default | Effect | |---|---|---| | `ADDTOPATH` | `1` | the installation folder in the system PATH, so new processes find `glog` | | `CONTEXTMENU` | `1` | *Analyse with G-Log* on folders, `.log`, `.gz` and `.zip` files | | `MASKSENSITIVE` | `0` | `1` masks client addresses, user names, passwords, tokens and card numbers in every report and MCP answer | | `DESKTOPSHORTCUT` | `0` | `1` puts a G-Log command prompt on the desktop, with the same icon as the start menu entry | | `SERVER` | `0` | `1` registers the service **GLogServer** (the report page and the API) and opens its port in the firewall for the private profile | | `SERVERPORT` | `7788` | the port of the service and of the firewall rule | | `AGENT` | `0` | `1` registers the service **GLogAgent** on a web server (not together with `SERVER`); see *An agent on every web server* | | `AGENTSERVER` | empty | `http://server:7788`: the agent sends its summaries there, and no inbound firewall rule is made. Empty: the server fetches, with an inbound rule on `SERVERPORT` | | `AGENTKEY` | empty | the key of that server, needed with `AGENTSERVER` | | `SERVICEACCOUNT` | empty | run the service under this account instead of LocalSystem: `DOMAIN\user`, `.\user`, a gMSA (`DOMAIN\name$`) or a virtual account (`NT SERVICE\GLogAgent`). See *A service account* | | `SERVICEPASSWORD` | empty | its password; not needed for a gMSA or a virtual account. A hidden property, so it does not end up in the installation log | | `INSTALLFOLDER` | `C:\Program Files\GariaNetTools\G-Log` | another folder | **Install from a local path.** The Windows Installer service runs as SYSTEM and cannot read a user's redirected folders. An MSI started from a Downloads folder on a network share or in OneDrive fails with **1619** (package cannot be opened), and a verbose log (`/l*v`) in a redirected `%TEMP%` fails with **1622** (log cannot be opened) - in both cases before anything is installed. Copy the MSI to `C:\Temp` or a deployment share the computer account can read, and write the log to a local folder: ```bat msiexec /i C:\Temp\G-Log-1.1.4-x64.msi /qn /l*v C:\Windows\Temp\glog-install.log ``` On a web server you usually want `CONTEXTMENU=0`: nobody browses there, and a script calls `glog` directly. `glog.exe` also runs without installing: copy it and go. **An update never needs a restart of the machine.** Whatever is running from the installation folder is made to step aside first: the services `GLogServer` and `GLogAgent` are stopped, and so is an agent or server that is not a service - a scheduled task, an RMM, a window someone left open, or `glog --mcp` behind an assistant. An agent and a server also carry a hidden window, so the Restart Manager of Windows can ask them to close the way it asks any other program. Afterwards the installer starts again what it stopped. What it cannot put back is an agent that ran under its own account: the credentials are not kept anywhere, and restarting it as SYSTEM would give it more rights than you gave it. That is written to `HKLM\Software\Garia.Net\G-Log\LastUpdate`, together with everything else that happened, so you can see why an agent is standing still. Start it again the way you started it the first time. Uninstalling removes the files, the PATH entry, the right-click menu, the start menu shortcuts and the desktop shortcut if there is one. Reports that the right-click menu wrote stay in each user's `%LOCALAPPDATA%\G-Log\Reports`. --- ## A service account The services **GLogServer** and **GLogAgent** run as LocalSystem unless you say otherwise. That works, but it is more than G-Log needs: it only reads. On a web server you usually want an account with read access to the log folders and nothing else. ```bat msiexec /i G-Log-x64.msi /qn AGENT=1 SERVICEACCOUNT="DOMAIN\svc-glog" SERVICEPASSWORD="..." ``` The installer first grants that account the right to **log on as a service** and then creates the service under it. That order matters: `sc.exe` does not grant the right, and without it the service fails to start with error 1069 - a message that does not say what is wrong. On an installation that already exists, the same by hand: ```bat glog --grant-service-logon "DOMAIN\svc-glog" sc config GLogAgent obj= "DOMAIN\svc-glog" password= "..." ``` A **virtual account** (`NT SERVICE\GLogAgent`) needs no password and no right: the service manager arranges both. A **gMSA** (`DOMAIN\name$`) needs the right but no password. What that account needs, and nothing more: | | | |---|---| | The log folders | read access. On IIS that is the folder `glog --sites` shows; on a Linux server over SSH it is the group `adm` | | `%ProgramData%\GariaNet\G-Log` | full access: the keys, the store and the audit trail live there | | The Windows event log | only if you keep alerts there, which is the default | | `applicationHost.config` | read access, to find the sites by name. Without it G-Log reads the folders you give it | It does **not** have to be an administrator, and it does not need to log on interactively. Granting the right and changing the service both need an administrator, so do that once at install time. **LocalSystem is not a superset of you.** It reaches other machines as the computer account, so a share you can open yourself may be refused, and its SSH keys live in its own profile. If an agent suddenly reads nothing after a change of account, look there first - and remember that what an account cannot read is missing from the report. G-Log says so in chapter 0 instead of leaving it out quietly. ### The firewall An agent and a server listen on the network, so Windows Firewall wants to know about them. The installers add a rule for the **private** profile when you choose the service (`SERVER=1` or `AGENT=1`); then nothing is asked. Start one by hand from a folder Windows has not seen before and it asks once, with *Public networks* already ticked - untick that and leave Private on. `--bind` says where to listen at all: ```bat glog --server --bind 10.0.0.5 only on the management card glog --agent --bind 127.0.0.1 only on this machine ``` Windows only asks about a program that listens beyond the loopback, so the second one is never asked about. An address that is not on this machine is refused with that as the reason. ## Finding the logs On a real server the logs are rarely where the documentation says. They move to a data partition because the system disk is too small, a site gets its own folder, and the number in `W3SVC` says nothing about which site it is. G-Log reads that from IIS itself: ```powershell glog --sites ``` ``` SITE ID LOG FOLDER Default Web Site 1 C:\inetpub\logs\LogFiles\W3SVC1 (site defaults) outsystems-dev 3 D:\inetpub\logs\LogFiles\W3SVC3 Intranet 7 \\logserver\iislogs$\W3SVC7 Stopped Site 9 C:\inetpub\logs\LogFiles\W3SVC9 (logging off) HTTP.sys errors: C:\Windows\System32\LogFiles\HTTPERR ``` It comes from `applicationHost.config`: the `logFile` of each site, or `siteDefaults` when a site has none of its own. A site that is not started, or has logging switched off, says so instead of being left out - an empty folder is otherwise a puzzle. **Another server** goes through its admin share, which is what you need from a jump server: ```powershell glog --sites \\web01 glog --report --site "Intranet" \\web01 --out report.html ``` Paths on `C:` become `\\web01\c$\...`, a path already written as UNC stays as it is, and `%SystemDrive%` is read as the `C:` of that machine. Reading `\\web01\c$\Windows\System32\inetsrv\config` needs an account that is an administrator there; without it the message says so instead of showing an empty list. ### Reading on the server, reporting elsewhere Copying gigabytes of logs over a jump server is slow, and reading them over an admin share is slower. A summary does the reading where the logs are and carries only the outcome: ```powershell # on web01 and on web02: the log folder of the site (glog --sites shows it), packed into one file glog --summary D:\iislogs\W3SVC7 --out C:\Temp\glog\web01.glogsum # on your own machine, after copying the two files glog --report web01.glogsum --report web02.glogsum --out intranet.html ``` The report has the same numbers as when the logs had been read in one go, and chapter 0 says which machine made each summary, from which folder, and when. What a summary does not carry is the log lines behind a finding: those stay on the server, and the report says so under every table that would have shown them. Search there with `glog --search`. Measured on generated logs: 980 MB of log gives a summary of 25.6 MB, four days in a folder (467 MB) 56 MB. Most of that is the tables of unique URLs and clients, so a site with many distinct URLs gives a larger file. ### One agent reading several web servers You do not need an agent on every machine. One agent on a jump server can read the log folders of several web servers over their admin share, and each folder becomes a site: ```cmd glog --agent --port 7931 --key --machine WEB01 --no-iis --no-context --agent-path "Shop=\\web01\d$\inetpub\logs\logfiles\W3SVC1" --store D:\glog\web01 glog --agent --port 7932 --key --machine WEB02 --no-iis --no-context --agent-path "Shop=\\web02\d$\inetpub\logs\logfiles\W3SVC3" --store D:\glog\web02 ``` Two things matter here. **`--machine`.** Without it an agent names itself after the computer it runs on, so two agents on the same jump server report the same machine name and the server sees one web server instead of two. **The name in front of the path.** A folder becomes a site named after that folder, so the same site under a different IIS id on each server (`W3SVC1` here, `W3SVC3` there) would give two separate sites, each with one machine. `Shop=` in front of the path names the site yourself, and both agents then report site *Shop*: one report, with the servers side by side in *Per server and site*. Leave the name out and the folder name is used, which is enough when the ids match. The same string works in the multi-string value `AgentPaths`. Reading over an admin share is slower than reading on the machine itself, and there is no server context (application pool crashes, deploys, certificates) because that needs an agent on the web server. For a cluster you watch every day, the next section is better. ### An agent on every web server, one server to look at The same thing, without copying files by hand: the service **GLogAgent** on each web server keeps the summaries up to date, and the service **GLogServer** on your jump server or management server collects them. **The server fetches** - the web servers are reachable from the server, for example from the jump server of a segment. On each web server: ```cmd msiexec /i G-Log-x64.msi /qb AGENT=1 /l*v C:\Temp\glog-install.log glog --show-key ``` The second line prints the key of that agent. On the server, one line per agent in the multi-string value `Agents`, then restart the service: ```cmd msiexec /i G-Log-x64.msi /qb SERVER=1 /l*v C:\Temp\glog-install.log reg add HKLM\Software\Garia.Net\G-Log /v Agents /t REG_MULTI_SZ /d "web01:7788 KEYOFWEB01\0web02:7788 KEYOFWEB02" /f sc.exe stop GLogServer sc.exe start GLogServer ``` **The agent sends** - nothing may come in on the web server (a cloud VM, a DMZ). On the server, make a one-time pairing key for each web server: ```cmd glog --pair-key --note "web03" ``` It is valid for an hour and works once. On that web server: ```cmd msiexec /i G-Log-x64.msi /qb AGENT=1 /l*v C:\Temp\glog-install.log glog --agent --push https://glogserver:7788 --pair ``` The agent gets a key of its own and remembers the certificate of the server; the pairing key is then worthless. That key of its own is good for delivering summaries and nothing else, so a web server never holds the key that opens the whole cluster. Without pairing it also still works with the key of the server (`glog --show-key` there): ```cmd msiexec /i G-Log-x64.msi /qb AGENT=1 AGENTSERVER=http://glogserver:7788 AGENTKEY=KEYOFTHESERVER /l*v C:\Temp\glog-install.log ``` Then, on the server: | To see | Ask | |---|---| | which agents answer, the last contact and how far their clock is off | `/api/agents` | | which sites there are and on how many machines | `/api/cluster/sites` | | the report of a site over all machines | `/?site=Intranet` in the browser, or `/api/report?site=Intranet` | | the requests behind a finding | `/api/search?site=Intranet&query=status:5xx` - passed on to the agents | A site is matched on its **name in IIS**: `Intranet` is `W3SVC7` on one machine and `W3SVC3` on the other, and that is fine. Give sites that belong together the same name. **What the web server notices.** The agent reads a log file only when it has changed, with one core at below-normal priority, and Windows holds its process to 10% of the processor (`AgentCpuPercent`). Measured: a 1 GB day took 1.3 s of processor time; held to 1% on a 32-thread machine it took 3.7 s instead of 1.3 s and used 0.84% on average. Summaries are kept for 7 days (`AgentKeepDays`), in `%ProgramData%\GariaNet\G-Log\store`. **When several machines serve one site,** the report of that site shows each machine as a server by name, and chapter 10 lists how far each machine's clock is off. A clock that is a minute or more off is a finding: requests on different servers no longer line up, and PCI DSS requires synchronised clocks. **Encrypted.** Agent and server speak https with a self-signed certificate that each machine makes for itself; they recognise each other by its fingerprint, which the server writes in `%ProgramData%\GariaNet\G-Log\keys.json` at the first contact. That file holds the keys as well, and only SYSTEM and Administrators can read it; keys that were in the registry move there by themselves. `glog --show-key` still prints the key of a machine, and `http://server:7788/api/hello` - or https - names the fingerprint, so you can compare the two by hand. For a trial on one machine, `--no-tls` runs without encryption. **How current it is.** The agent reads only what was added to a log since its last round, so a round of 15 seconds (`AgentInterval`) costs little even on a large log. IIS writes its log buffer about once a minute, so the server is at most about a minute behind. A rotated or replaced log is noticed and read again. **Not yet:** installing an agent from your own session (PLAN §12) and following within seconds through ETW come later. ### A Linux web server, without an agent nginx and Apache mostly run on Linux, where there is no G-Log to install. Give the logs as an address instead: ```cmd glog --report ssh://deploy@web03/var/log/nginx --ssh-key C:\Keys\glog_ed25519 --out D:\reports\web03.html ``` G-Log signs in with the `ssh` that comes with Windows, lists the logs and fetches them to this machine, and after that reads them as a folder. **Only what was added since the last time goes over the line**, so a daily round on a busy server costs little; a log that was rotated is fetched again in full. The account on that server needs nothing but read access to the logs - on Debian and Ubuntu that means the group `adm`. A host key that is not known yet is remembered, a changed one is refused. `--ssh-known-hosts` puts that file somewhere else, which a service account with no profile needs. #### Signing in with a password A key is the better way in, but not every environment hands you one. Since 1.0.2 a password works too: ```cmd glog --report ssh://deploy@web03/var/log/nginx --ssh-password --out D:\reports\web03.html ``` That asks for the password and does not echo it. For a service or a scheduled task, store it once: ```cmd glog --ssh-save-password deploy@web03 glog --ssh-passwords lists the targets that have one, never the password glog --ssh-forget-password deploy@web03 removes it ``` It goes into `%ProgramData%\GariaNet\G-Log\keys.json` with the other keys, a file only SYSTEM and Administrators can read, and an agent with an `ssh://` source picks it up by itself. A target is `user@host:port`; leaving the user out of the address works as long as only one user on that host has a password stored. **A password is never a value on the command line** - that would put it in the process list and in the shell history. `ssh.exe` does not take one from a pipe either, on purpose, so G-Log answers the question the way OpenSSH means it to be answered: through `SSH_ASKPASS`, with `glog.exe` itself as the helper, and the password only in the environment of that one ssh process. Without a password `BatchMode` still holds, so a service never waits on a question no one will see. Anyone who is already an administrator on this machine can read a stored password; that is the trade for reaching a server that offers nothing else. The agent takes the same address: `--agent-path ssh://deploy@web03/var/log/nginx` (or in the multi-string value `AgentPaths`). Then that Linux server is a site in the cluster like any other, read by the agent on a Windows machine that can reach it. ### Alerts The server checks a small set of rules every minute and says when something is wrong, with the numbers: one machine with far more server errors than the rest of the site, a machine that stopped logging, an agent the server no longer hears, HTTP.sys refusing requests from a full queue, a wave of attack patterns, a card number in a URL, and a site that is much slower than it usually is at this hour. A rule fires only when it holds for a while, and is resolved only after a margin, so it does not flicker. The rules and their thresholds are in [`api/API.md`](../api/API.md#alerts). Every alert goes to the **Windows event log** (*Application*, source *G-Log*), which is how NinjaOne, SCOM or a SIEM picks it up without anything extra: | Event ID | Alert | Resolved | |---|---|---| | 1001 | one machine has far more server errors than the others | 1101 | | 1002 | a machine stopped logging | 1102 | | 1003 | the server no longer hears an agent | 1103 | | 1004 | HTTP.sys refuses requests | 1104 | | 1005 | a wave of attack patterns | 1105 | | 1006 | a card number in a URL (error) | 1106 | | 1007 | a worker process crashed or stopped responding; error when rapid-fail protection stopped the app pool | 1107 | | 1008 | a certificate expires within 30 days (error within 7, expired, or missing from the store) | 1108 | | 1009 | a log volume is almost full | 1109 | | 1010 | a site is at least twice as slow as usual at this hour (error at four times); needs 5 earlier days to learn | 1110 | **What the agent sees on the server itself.** A weblog says *that* requests failed, not that the app pool crashed three minutes after a deploy. The agent reads that on the server: WAS events and `w3wp` crashes from the event log, changed `web.config`, `appsettings*.json` and `bin\*.dll` in the folder of each site, the certificates of the HTTPS bindings, processor, HTTP.sys queue and free disk space, and the processor and memory of the worker process of each app pool. The report of a site on the server lists it under *Server events*, with a finding when a worker process failed or a certificate expires within 30 days. Reading the System and Application logs needs no extra rights for the service account LocalSystem; tested here with events and certificates made for the test, not on a real IIS server. In NinjaOne that is a *Windows Event* condition on the server: source `G-Log`, the event IDs you care about. A **webhook** (Teams, Slack or your own endpoint) and **syslog** (a SIEM, as CEF) are a registry value away, then restart the service: ```cmd reg add HKLM\Software\Garia.Net\G-Log /v AlertWebhook /t REG_SZ /d "https://example.webhook.office.com/..." /f reg add HKLM\Software\Garia.Net\G-Log /v AlertSyslog /t REG_SZ /d "siem01:514" /f sc.exe stop GLogServer sc.exe start GLogServer ``` Open the server in a browser (`https://server:7788/?token=`; the browser warns about the self-signed certificate, whose fingerprint `/api/hello` names) for the same in a page, refreshed every minute: what fires, what was resolved in the last day, the sites with their report, and the agents with their last contact. Crashes and deploys are marked in the charts of a site's report. `/api/alerts` on the server lists what is firing and what was resolved in the last day, each with a link to the page of that site. Tested here with a server, four agents and generated logs of this minute, a webhook receiver and a syslog receiver; the event log itself was left out of that test, so that check is still to be done on a server. --- ## Sending the lines to your SIEM An agent can forward the log lines it reads to the SIEM or log server your organisation already has. G-Log does not replace that store; it delivers to it (PCI DSS 10.3.3). ``` glog --agent --forward udp://siem.example:514 glog --agent --forward tcp://siem.example:601 --forward-what "status:5xx" --forward-format cef ``` Lines go out over syslog as they stand, or as CEF with the line in `msg`. `--forward-what` takes the same query language as `--search`. UDP is what most SIEMs expect and loses a line when the network hiccups; TCP does not. Only what is new since the last round goes out. The status of the agent shows how many lines went and what went wrong, if anything. ## Reading along within seconds By default an agent follows the log file, and HTTP.sys writes that once a minute or at 64 KB - so you are up to a minute behind. On IIS 8.5 and later you can have IIS send every request to ETW as well, and then an agent reads along immediately: 1. In IIS Manager, open the site, then **Logging**, and set **Log Event Destination** to *ETW event only* or *Both log file and ETW event*. G-Log never changes this for you. 2. Run the agent with `--etw`. It needs Administrator or membership of *Performance Log Users*. The log file stays the source that always works; ETW comes on top of it. If ETW cannot start, G-Log says why and carries on with the file. ## When your log format is your own nginx and Apache let you define any format you like. The usual ones G-Log reads by itself, including nginx logs written as JSON. For your own, hand it the line from your configuration: ``` glog --report access.log --log-format "%h %l %u %t \"%r\" %>s %b \"%{User-Agent}i\" %D" glog --report access.log --log-format /etc/nginx/nginx.conf --log-format-name main ``` The second form reads the `log_format` (nginx) or `LogFormat` (Apache) line out of the configuration itself, so there is nothing to keep in sync. For a log you read over SSH, give the line rather than the path: the path would be looked for on this machine. ## The daily review A server writes the review of each day that has passed: the findings of that day, what their status is, and who signed the day off. It is on the **PCI** tab of the page, and on the command line: ``` glog --reviews the last thirty days glog --review --day 2026-09-17 one day in full glog --finding pci.cardNumbers --status accepted --sign-off "Your Name" --note-text "known test data" glog --sign-off "Your Name" --note-text "daily review done" ``` Each file lives in `%ProgramData%\GariaNet\G-Log\reviews\`, one per day, so it can be archived per day. Nothing is overwritten: every status change is added to the history with a name, the Windows account and a note. A name is not an account - G-Log has no user accounts - and the page says so where you sign. The same view shows how far the history of this server reaches, against the three months that PCI wants immediately available and the twelve months it wants kept. G-Log keeps its own summaries, not your raw logs; keep those where your organisation keeps them. ## For an auditor: sealed logs and who looked An agent gives every log file a SHA-256 as soon as it is finished, and checks each round that it is still the same file. `glog --integrity` shows that register and ends with exit code 1 when a file changed after it was closed or disappeared - handy as a check in your monitoring. The register is plain JSON in the store folder, readable without G-Log. A server or agent that listens on the network also keeps `audit.log`: one line per report, search, export or setting that was asked for, with the time, the address, the key that was used and the result. `glog --audit` shows the last lines. It records the **key**, not a person - G-Log has no accounts - and never the key itself. Turn it off with `--no-audit` if your own logging already covers this. ## The page of the server Open `https://:8642/?token=` once and the key is remembered in a cookie for that browser. Behind the tabs on that page: - **Search** - a question about a site (`status:5xx url:/api/* since:24h`), answered by the web servers themselves, with the columns of the log. The address holds the question, so you can send a colleague a link to exactly what you are looking at. - **Reports** - what stands in `reports.json`, when each one runs next, and the reports that have been made. One click opens one. - **Settings** - version, role, port, TLS with its fingerprint, the round, how long summaries are kept, and where the key file, the data folder and the schedules are. Keys are never on the page; use `glog --show-key` on the machine itself. The overview refreshes itself every minute, the other views do not: what you are reading stays where it is. ## A report every morning The service can make reports by itself. Put the schedules in `%ProgramData%\GariaNet\G-Log\reports.json`: ```json { "reports": [ { "name": "shop-daily", "site": "Shop", "every": "day", "at": "06:00", "period": "yesterday", "format": "html", "out": "\\\\fileserver\\reports$\\glog", "keep": 30 }, { "name": "shop-weekly", "site": "Shop", "every": "week", "on": "monday", "at": "07:30", "period": "lastWeek", "format": "html", "out": "\\\\fileserver\\reports$\\glog", "keep": 12 } ] } ``` Times are UTC. The report lands as `shop-daily-20260918.html`, a single file you can mail or print, and `keep` says how many of them stay. A source is a site of the cluster, a folder, or an `ssh://` address; the format can also be `json`, `csv` or `text` for a script. The account of the service needs write access to that folder or share. `glog --reports` shows what stands there and when each one runs next, and `glog --report-now shop-daily` makes one right away - handy to see that the path and the rights are good before you wait until six. ## Which fields to log IIS logs a limited set of fields by default. Several analyses need more, and chapter 0 lists which fields are missing and what that rules out. | Field | Without it | |---|---| | `time-taken` | no response times, no slowest pages, no regressions, no Apdex | | `cs-host` | no split per host name | | `sc-substatus`, `sc-win32-status` | a 500 or 404 without its cause; no dropped connections | | `cs(User-Agent)` | no browsers, bots or attack tools | | `cs(Referer)` | no referring sites, landing pages or broken links from elsewhere | | `sc-bytes`, `cs-bytes` | no bandwidth | | `X-Forwarded-For` (custom field) | behind a load balancer every visitor is the balancer's address | To turn them on for all sites, from an elevated prompt on the server. This is standard IIS configuration; **it has not been run on this machine**, which has no IIS: ```bat %windir%\system32\inetsrv\appcmd set config -section:system.applicationHost/sites ^ -siteDefaults.logFile.logExtFileFlags:"Date,Time,ClientIP,UserName,SiteName,ComputerName,ServerIP,Method,UriStem,UriQuery,HttpStatus,Win32Status,BytesSent,BytesRecv,TimeTaken,ServerPort,UserAgent,Referer,ProtocolVersion,Host,HttpSubStatus" ^ /commit:apphost %windir%\system32\inetsrv\appcmd set config -section:system.applicationHost/sites ^ /+"siteDefaults.logFile.customFields.[logFieldName='X-Forwarded-For',sourceName='X-Forwarded-For',sourceType='RequestHeader']" ^ /commit:apphost ``` A site with its own logging settings overrides the defaults; set the same flags on that site instead. ### Apache and nginx Both log the Combined Log Format by default, which has no response time: then there are no response times, no slowest pages and no Apdex, and chapter 0 says so. G-Log reads a response time as the last field. Keep the rest of the format as it is. ```apache # Apache: %D is the time in microseconds LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\" %D" combined_time CustomLog ${APACHE_LOG_DIR}/access.log combined_time ``` ```nginx # nginx: $request_time is the time in seconds, with milliseconds log_format combined_time '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" $request_time'; access_log /var/log/nginx/access.log combined_time; ``` Apache's `vhost_combined` (`%v:%p` in front) and `common` are read too; `vhost_combined` gives the split per site. Tested on nginx 1.24 and Apache 2.4.58 with these formats (and the defaults), under other format names; G-Log itself runs on Windows, so copy the logs over or read them from a share. --- ## Finding the requests behind a problem The report tells you *that* something is wrong and where. To fix it you need the requests themselves: 1. **In the report**, ▸ in front of a row opens the log lines behind it - for a URL with server errors, a status code, a client, a slow page, a bad stretch of minutes, a login attack or a secret in a URL. Each line comes with its file and line number, so you can open the log at that exact place. 2. **With `glog --search`**, any other set of lines, as fast as the report reads the log: ```powershell # the server errors of the incident the report found glog --search C:\inetpub\logs\LogFiles\W3SVC1 "status:5xx from:2026-09-11T10:00 to:2026-09-11T10:29" # what one client did, as a table for a ticket glog --search C:\inetpub\logs\LogFiles\W3SVC1 "client:10.51.141.11" --format csv --out client.csv # slow requests that were not downloads, with a substatus that says why glog --search C:\inetpub\logs\LogFiles\W3SVC1 "time>10s -url:/download/*" --limit 50 # the server errors of one caller: the script or integration the report named glog --search C:\inetpub\logs\LogFiles\W3SVC1 "ua:*python-requests* status>=500" --limit 50 ``` The query language is in the [README](../README.md#searching-the-log). With `MASKSENSITIVE=1` or `--mask`, search results are masked the same way as the report. **On a server with months of daily logs**, add `--latest` to read only the newest log (or `--latest 3` for the three newest) and `--out C:\Temp\report.html` to say where the page goes. `--threads 2` keeps the memory down on a machine that is already short of it. `glog --open` works without a browser registered as well: it writes the report, prints its path, and tries Microsoft Edge and the Windows file handler before it gives up. --- ## Scripts and monitoring The exit code is the contract: | Code | Meaning | |---|---| | 0 | the report is written | | 1 | reading or writing failed; the reason is on standard error | | 2 | wrong arguments | | 3 | the report is written, and it has advice at or above the `--fail-on` severity | Scripts call **`glog.com`**, next to `glog.exe`: it starts `glog.exe` with the same arguments, waits, passes the exit code on, and stops it when the script is stopped. The JSON goes to standard output when there is no `--out`, so a PowerShell script can use it directly: ```powershell $glog = "C:\Program Files\GariaNetTools\G-Log\glog.com" [Console]::OutputEncoding = [Text.Encoding]::UTF8 $r = (& $glog --report C:\inetpub\logs\LogFiles\W3SVC1 --since 1h --with-text) -join "`n" | ConvertFrom-Json "{0} requests, {1} critical, {2} warnings" -f $r.summary.requests, $r.summary.critical, $r.summary.warning $r.advice | Where-Object severity -ne 'info' | Select-Object severity, title, do ``` | For a script | Option | |---|---| | only the last stretch of time; files last written before it are skipped | `--since 15m` (or `2h`, `7d`), or `--from 2026-09-15T07:00 --to 2026-09-15T07:30` (UTC) | | only today's log on a server with months of daily logs | `--latest` (or `--latest 3` for the three newest) | | less memory on a machine that is already short of it | `--threads 2`; G-Log reads a big log with all cores by default | | a plain-English summary for an alert text or a ticket | `--format text` | | one table for a spreadsheet, such as the status codes | `--format csv --table errors.statusCodes`; `--tables` lists them | | a CSV that Excel opens in columns on a Dutch, French, Spanish or Portuguese Windows | add `--csv-sep ;` (or `--csv-sep tab`) | | the English title and what to do next to each finding in the JSON | `--with-text` | | only the exit code | `--out NUL` | A CSV cell that a spreadsheet would read as a formula (starting with `=`, `+`, `-` or `@`) gets an apostrophe in front, and a CSV file starts with a UTF-8 byte order mark so Excel reads accents correctly. Standard output has no mark. **NinjaOne:** [`ninjaone/README.md`](ninjaone/README.md) has three ready-made scripts - an alert on the last 15 minutes, a daily report into custom fields with an HTML report, and a search - and how to set them up. They were tested under Windows PowerShell 5.1 with a stand-in for NinjaOne's field command. **They have not been run inside NinjaOne.** Advice has stable codes (`errors.serverErrorRate`, `security.loginAttack`, `change.regressedUrl`) and the measured values alongside, so a script can act on a code without parsing a sentence. The full list is in [`../api/API.md`](../api/API.md). --- ## An AI assistant on your logs `glog --mcp` lets an assistant such as Claude query the logs on the machine it runs on. Keep in mind: - **It runs as you.** It can read what your account can read, and nothing else. - **It only reads.** It writes an HTML report only when started with `--allow-write`, and then only to a file that does not exist yet. It never overwrites. - **Nothing leaves the machine through G-Log.** The assistant does receive the answers: numbers, URLs, client addresses. Where those go next is up to the assistant you use. - **Answers are measured, not guessed.** Every number comes from the same report as the page, and an analysis the log cannot support says why. For Claude Code: `claude mcp add glog -- "C:\Program Files\GariaNetTools\G-Log\glog.exe" --mcp`. --- ## When the numbers look wrong - **Times are UTC.** IIS writes W3C times in UTC, also with *local time rollover* (that only changes when a new file starts); chapter 10 checks the clock and the rollover. Apache and nginx write local time with the offset (`[15/Sep/2026:19:01:27 +0200]`); G-Log converts every line to UTC, so a report of servers in different time zones lines up. - **One server behind a balancer looks like one client.** Log `X-Forwarded-For`; chapter 5 warns when one address sends most of the traffic. - **"Needs at least N days".** Week patterns, growth and the error budget need a folder with several daily logs, not one file. - **Mixed server identity.** When only some lines have `s-computername`, the traffic per server carries the note `serverIdentityMixed`, and G-Log gives no advice about balance or a slower server rather than guess from half the data.