1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
11 "git.arvados.org/arvados.git/lib/ctrlctx"
12 "git.arvados.org/arvados.git/sdk/go/arvados"
13 "git.arvados.org/arvados.git/sdk/go/ctxlog"
16 func (conn *Conn) logActivity(ctx context.Context) {
17 p := conn.cluster.Users.ActivityLoggingPeriod.Duration()
19 ctxlog.FromContext(ctx).Debug("logActivity disabled by config")
22 user, _, err := ctrlctx.CurrentAuth(ctx)
23 if err == ctrlctx.ErrUnauthenticated {
24 ctxlog.FromContext(ctx).Debug("logActivity skipped for unauthenticated request")
26 } else if err != nil {
27 ctxlog.FromContext(ctx).WithError(err).Error("logActivity CurrentAuth failed")
31 conn.activeUsersLock.Lock()
32 if conn.activeUsers == nil || conn.activeUsersReset.IsZero() || conn.activeUsersReset.Before(now) {
33 conn.activeUsersReset = alignedPeriod(now, p)
34 conn.activeUsers = map[string]bool{}
36 logged := conn.activeUsers[user.UUID]
38 // Prevent other concurrent calls from logging about
39 // this user until we finish.
40 conn.activeUsers[user.UUID] = true
42 conn.activeUsersLock.Unlock()
47 // If we return without logging, reset the flag so we
48 // try again on the user's next API call.
50 conn.activeUsersLock.Lock()
51 conn.activeUsers[user.UUID] = false
52 conn.activeUsersLock.Unlock()
56 tx, err := ctrlctx.NewTx(ctx)
58 ctxlog.FromContext(ctx).WithError(err).Error("logActivity NewTx failed")
62 _, err = tx.ExecContext(ctx, `
65 owner_uuid, modified_by_user_uuid, object_owner_uuid,
70 event_at, created_at, updated_at, modified_at)
72 ($1, $2, $2, $2, $3, $4, $5, $6,
73 current_timestamp at time zone 'UTC',
74 current_timestamp at time zone 'UTC',
75 current_timestamp at time zone 'UTC',
76 current_timestamp at time zone 'UTC')
78 arvados.RandomUUID(conn.cluster.ClusterID, "57u5n"),
79 conn.cluster.ClusterID+"-tpzed-000000000000000", // both modified_by and object_owner
81 "activity of "+user.UUID,
85 ctxlog.FromContext(ctx).WithError(err).Error("logActivity query failed")
90 ctxlog.FromContext(ctx).WithError(err).Error("logActivity commit failed")
96 // alignedPeriod computes a time interval that includes now and aligns
97 // to local clock times that are multiples of p. For example, if local
98 // time is UTC-5 and ActivityLoggingPeriod=4h, periodStart and
99 // periodEnd will be 0000-0400, 0400-0800, etc., in local time. If p
100 // is a multiple of 24h, periods will start and end at midnight.
102 // If DST starts or ends during this period, the boundaries will be
103 // aligned based on either DST or non-DST time depending on whether
104 // now is before or after the DST transition. The consequences are
105 // presumed to be inconsequential, e.g., logActivity may unnecessarily
106 // log activity more than once in a period that includes a DST
109 // In all cases, the period ends in the future.
111 // Only the end of the period is returned.
112 func alignedPeriod(now time.Time, p time.Duration) time.Time {
113 _, tzsec := now.Zone()
114 tzoff := time.Duration(tzsec) * time.Second
115 periodStart := now.Add(tzoff).Truncate(p).Add(-tzoff)
116 return periodStart.Add(p)