Summary
When a spec uses container sharing (container-pre-initialization / seats), the "Report Issue" mail names a container log file that does not exist, and can never exist. The logs themselves are written correctly — only the path the mail reports is wrong. I would like to know whether this is an oversight or an accepted limitation of combining container sharing with proxy.container-log-path, before I offer a PR.
Environment
- ShinyProxy 3.2.4 (containerproxy 1.2.4)
- Kubernetes backend, 2 replicas,
store-mode: Redis
proxy.container-log-path: s3://<bucket>/<prefix>/
- The spec uses container sharing, with the app pods pre-initialized
What happens
A user reports an issue from a shared-seat app. The mail says:
AppId: 32a96d14-2dde-4839-895a-af3a581d2482
App: rating
Log (stdout): s3:/<bucket>/<prefix>/rating_32a96d14-2dde-4839-895a-af3a581d2482_15_Sep_2026_17_24_50_stdout.log
Log (stderr): s3:/<bucket>/<prefix>/rating_32a96d14-2dde-4839-895a-af3a581d2482_15_Sep_2026_17_24_50_stderr.log
Neither object exists (both HeadObject calls return 404), and no object anywhere in the bucket contains that proxy id. The log that does exist is:
<prefix>/rating_c827815f-7ce0-472f-8541-6d38dde033b9_15_Sep_2026_16_57_30_stdout.log
c827815f-… is the delegate proxy, which ShinyProxy itself logged when the seat was claimed:
ProxySharingDispatcher : [user=… proxyId=32a96d14-… specId=rating delegateProxyId=c827815f-… seatId=…] Seat claimed
It is also the pod that ran the app (sp-pod-c827815f-…-0), so the container, the pod and the log file all agree with each other — the mail is the only thing that disagrees.
Where it comes from, as far as I can tell
Two things combine:
IssueController.sendSupportMail calls logService.getLogs(proxy) with the user-facing proxy, and AbstractLogStorage.getLogs derives both the cache key and the file name from proxy.getId(). But LogService attached the output for the delegate proxy, which is what owns the container, so the file was written under the delegate's id. The two ids can never coincide once a spec shares containers.
- The name embeds a timestamp minted lazily inside
computeIfAbsent, so any instance that did not attach the output invents a fresh one. With more than one replica, the instance handling POST /issue is frequently not the instance that opened the file — the timestamp in the example above is the moment the user pressed the button, not the moment the log was opened.
For (1) it looks like Proxy#getTargetId() is already exactly the right value: DefaultProxyDispatcher sets TargetIdKey to the proxy's own id, and ProxySharingDispatcher sets it to the delegate proxy's id, so one accessor covers both cases without special-casing.
Related, and possibly intentional
On a filesystem container-log-path the mail attaches the log; on S3 it can only name it, because IssueController tests filePaths.getStdout().toFile().exists(), which an S3 key never satisfies. Is that also deliberate? If so, I understand the reasoning — but combined with the above, an operator receiving a support mail from a shared-seat app currently has no usable route from the mail to the logs.
Also minor: the path is printed as s3:/bucket/... with a single slash, because Paths.get normalises the // away.
Question
Is (1) a miss, or is container logging understood not to apply to shared containers? Same question for the multi-replica timestamp in (2).
If you would take patches, I am happy to open them — I would expect (1) to be a small change around getTargetId(), and (2) to need the resolved names carried on the proxy rather than recomputed. Glad to follow whatever shape you would prefer.
Summary
When a spec uses container sharing (
container-pre-initialization/ seats), the "Report Issue" mail names a container log file that does not exist, and can never exist. The logs themselves are written correctly — only the path the mail reports is wrong. I would like to know whether this is an oversight or an accepted limitation of combining container sharing withproxy.container-log-path, before I offer a PR.Environment
store-mode: Redisproxy.container-log-path: s3://<bucket>/<prefix>/What happens
A user reports an issue from a shared-seat app. The mail says:
Neither object exists (both
HeadObjectcalls return 404), and no object anywhere in the bucket contains that proxy id. The log that does exist is:c827815f-…is the delegate proxy, which ShinyProxy itself logged when the seat was claimed:It is also the pod that ran the app (
sp-pod-c827815f-…-0), so the container, the pod and the log file all agree with each other — the mail is the only thing that disagrees.Where it comes from, as far as I can tell
Two things combine:
IssueController.sendSupportMailcallslogService.getLogs(proxy)with the user-facing proxy, andAbstractLogStorage.getLogsderives both the cache key and the file name fromproxy.getId(). ButLogServiceattached the output for the delegate proxy, which is what owns the container, so the file was written under the delegate's id. The two ids can never coincide once a spec shares containers.computeIfAbsent, so any instance that did not attach the output invents a fresh one. With more than one replica, the instance handlingPOST /issueis frequently not the instance that opened the file — the timestamp in the example above is the moment the user pressed the button, not the moment the log was opened.For (1) it looks like
Proxy#getTargetId()is already exactly the right value:DefaultProxyDispatchersetsTargetIdKeyto the proxy's own id, andProxySharingDispatchersets it to the delegate proxy's id, so one accessor covers both cases without special-casing.Related, and possibly intentional
On a filesystem
container-log-paththe mail attaches the log; on S3 it can only name it, becauseIssueControllertestsfilePaths.getStdout().toFile().exists(), which an S3 key never satisfies. Is that also deliberate? If so, I understand the reasoning — but combined with the above, an operator receiving a support mail from a shared-seat app currently has no usable route from the mail to the logs.Also minor: the path is printed as
s3:/bucket/...with a single slash, becausePaths.getnormalises the//away.Question
Is (1) a miss, or is container logging understood not to apply to shared containers? Same question for the multi-replica timestamp in (2).
If you would take patches, I am happy to open them — I would expect (1) to be a small change around
getTargetId(), and (2) to need the resolved names carried on the proxy rather than recomputed. Glad to follow whatever shape you would prefer.