From 416f742cdbfb9c16404c223f2524bade32f100fb Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Apr 05 2025 01:00:54 +0000
Subject: [PATCH 1/4] scheduler-logs --watch option


---

diff --git a/cli/koji_cli/commands.py b/cli/koji_cli/commands.py
index 114d530..4cb34f4 100644
--- a/cli/koji_cli/commands.py
+++ b/cli/koji_cli/commands.py
@@ -8417,6 +8417,8 @@ def handle_scheduler_logs(goptions, session, args):
                       help="Logs until given timestamp (included)")
     parser.add_option("--limit", action="store", type=int, default=None,
                       help="Limit data to last N items. [default: %default]")
+    parser.add_option("--watch", action="store_true",
+                      help="Monitor scheduler logs")
     (options, args) = parser.parse_args(args)
     if len(args) != 0:
         parser.error("There are no arguments for this command")
@@ -8436,31 +8438,21 @@ def handle_scheduler_logs(goptions, session, args):
         clauses.append(['msg_ts', '>=', options.from_ts])
     if options.to_ts:
         clauses.append(['msg_ts', '<', options.to_ts])
+    if options.watch:
+        if session.hub_version < (1, 35, 0):
+            parser.error('This option is not supported with hub version %s' % session.hub_version_str)
+        if options.to_ts:
+            parser.error('The --watch option cannot be used with --to')
 
     fields = ('id', 'task_id', 'host_id', 'host_name', 'msg_ts', 'msg')
     kwargs = {'clauses': clauses, 'fields': fields}
     if session.hub_version < (1, 34, 0):
         error("Hub version is %s and doesn't support scheduler methods "
               "introduced in 1.34." % session.hub_version_str)
-    if options.limit is not None:
-        if session.hub_version >= (1, 34, 1):
-            kwargs['opts'] = {'order': '-id', 'limit': options.limit}
-    logs = session.scheduler.getLogMessages(**kwargs)
 
     if options.limit is not None:
         if session.hub_version >= (1, 34, 1):
-            # server did it for us, but we need to reverse
-            # don't use reversed() as it will be exhausted after modification loop later
-            logs.reverse()
-        else:
-            # emulate limit
-            logs = logs[-options.limit:]
-    if session.hub_version < (1, 34, 1):
-        # emulate order
-        logs.sort(key=lambda r: r['id'])
-
-    for log in logs:
-        log['time'] = time.asctime(time.localtime(log['msg_ts']))
+            kwargs['opts'] = {'order': '-id', 'limit': options.limit}
 
     mask = ("%(task_id)-10s %(host_name)-20s %(time)-25s %(msg)-30s")
     if not goptions.quiet:
@@ -8473,8 +8465,38 @@ def handle_scheduler_logs(goptions, session, args):
         print(h)
         print('-' * len(h))
 
-    for log in logs:
-        print(mask % log)
+    while True:
+        logs = session.scheduler.getLogMessages(**kwargs)
+
+        if options.limit is not None:
+            if session.hub_version >= (1, 34, 1):
+                # server did it for us, but we need to reverse
+                # don't use reversed() as it will be exhausted after modification loop later
+                logs.reverse()
+            else:
+                # emulate limit
+                logs = logs[-options.limit:]
+        if session.hub_version < (1, 34, 1):
+            # emulate order
+            logs.sort(key=lambda r: r['id'])
+
+        for log in logs:
+            log['time'] = time.asctime(time.localtime(log['msg_ts']))
+
+        for log in logs:
+            print(mask % log)
+
+        if not options.watch:
+            break
+        else:
+            if logs:
+                # update query for next pass
+                last_id = logs[-1]['id']
+                options.limit = None
+                kwargs['opts'] = {'order': 'id'}
+                kwargs['clauses'] = clauses + [['id', '>', last_id]]
+            time.sleep(goptions.poll_interval)
+            continue
 
 
 def handle_promote_build(goptions, session, args):

From 1d97c429eb133d2dd997e4ebe03caf9b9da4437e Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Apr 05 2025 01:00:54 +0000
Subject: [PATCH 2/4] add more options, adjust time display


---

diff --git a/cli/koji_cli/commands.py b/cli/koji_cli/commands.py
index 4cb34f4..f5ca09e 100644
--- a/cli/koji_cli/commands.py
+++ b/cli/koji_cli/commands.py
@@ -8411,6 +8411,14 @@ def handle_scheduler_logs(goptions, session, args):
                       help="Filter by task ID")
     parser.add_option("--host", type="str", action="store",
                       help="Filter by host (name/ID)")
+    parser.add_option("--channel", type="str", action="store",
+                      help="Filter by task channel (name/ID)")
+    parser.add_option("--arch", type="str", action="store",
+                      help="Filter by task arch")
+    parser.add_option("--method", type="str", action="store",
+                      help="Filter by task method")
+    parser.add_option("--owner", type="str", action="store",
+                      help="Filter by task owner (name/ID)")
     parser.add_option("--from", type="float", action="store", dest="from_ts",
                       help="Logs from given timestamp")
     parser.add_option("--to", type="float", action="store", dest="to_ts",
@@ -8432,8 +8440,24 @@ def handle_scheduler_logs(goptions, session, args):
         try:
             host_id = int(options.host)
         except ValueError:
-            host_id = session.getHost(options.host)['id']
+            host_id = session.getHost(options.host, strict=True)['id']
         clauses.append(['host_id', host_id])
+    if options.channel:
+        try:
+            channel_id = int(options.channel)
+        except ValueError:
+            channel_id = session.getChannel(options.channel, strict=True)['id']
+        clauses.append(['channel_id', channel_id])
+    if options.arch:
+        clauses.append(['arch', options.arch])
+    if options.method:
+        clauses.append(['method', options.method])
+    if options.owner:
+        try:
+            owner_id = int(options.owner)
+        except ValueError:
+            owner_id = session.getUser(options.owner, strict=True)['id']
+        clauses.append(['owner', owner_id])
     if options.from_ts:
         clauses.append(['msg_ts', '>=', options.from_ts])
     if options.to_ts:
@@ -8454,7 +8478,7 @@ def handle_scheduler_logs(goptions, session, args):
         if session.hub_version >= (1, 34, 1):
             kwargs['opts'] = {'order': '-id', 'limit': options.limit}
 
-    mask = ("%(task_id)-10s %(host_name)-20s %(time)-25s %(msg)-30s")
+    mask = ("%(time)-20s %(task_id)-10s %(host_name)-30s %(msg)-30s")
     if not goptions.quiet:
         h = mask % {
             'task_id': 'Task',
@@ -8481,7 +8505,7 @@ def handle_scheduler_logs(goptions, session, args):
             logs.sort(key=lambda r: r['id'])
 
         for log in logs:
-            log['time'] = time.asctime(time.localtime(log['msg_ts']))
+            log['time'] = koji.formatTime(log['msg_ts'])
 
         for log in logs:
             print(mask % log)
@@ -8496,7 +8520,6 @@ def handle_scheduler_logs(goptions, session, args):
                 kwargs['opts'] = {'order': 'id'}
                 kwargs['clauses'] = clauses + [['id', '>', last_id]]
             time.sleep(goptions.poll_interval)
-            continue
 
 
 def handle_promote_build(goptions, session, args):

From 832101e32fc9a92a77ad9b0faa85ccf8509fa4d3 Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Apr 07 2025 21:31:28 +0000
Subject: [PATCH 3/4] trying a different output format


---

diff --git a/cli/koji_cli/commands.py b/cli/koji_cli/commands.py
index f5ca09e..cedebb3 100644
--- a/cli/koji_cli/commands.py
+++ b/cli/koji_cli/commands.py
@@ -8478,16 +8478,7 @@ def handle_scheduler_logs(goptions, session, args):
         if session.hub_version >= (1, 34, 1):
             kwargs['opts'] = {'order': '-id', 'limit': options.limit}
 
-    mask = ("%(time)-20s %(task_id)-10s %(host_name)-30s %(msg)-30s")
-    if not goptions.quiet:
-        h = mask % {
-            'task_id': 'Task',
-            'host_name': 'Host',
-            'time': 'Time',
-            'msg': 'Message',
-        }
-        print(h)
-        print('-' * len(h))
+    mask = ("%(time)s %(task_id)s %(host_name)s - %(msg)s")
 
     while True:
         logs = session.scheduler.getLogMessages(**kwargs)

From a3b02d6898293317b9bf8dee452d71bdbcfbe328 Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Apr 07 2025 21:37:09 +0000
Subject: [PATCH 4/4] friendlier time options


---

diff --git a/cli/koji_cli/commands.py b/cli/koji_cli/commands.py
index cedebb3..7da3b62 100644
--- a/cli/koji_cli/commands.py
+++ b/cli/koji_cli/commands.py
@@ -8406,7 +8406,7 @@ def anon_handle_scheduler_info(goptions, session, args):
 def handle_scheduler_logs(goptions, session, args):
     "[monitor] Query scheduler logs"
     usage = "usage: %prog scheduler-logs <options>"
-    parser = OptionParser(usage=get_usage_str(usage))
+    parser = OptionParser(usage=get_usage_str(usage), option_class=TimeOption)
     parser.add_option("--task", type="int", action="store",
                       help="Filter by task ID")
     parser.add_option("--host", type="str", action="store",
@@ -8419,9 +8419,9 @@ def handle_scheduler_logs(goptions, session, args):
                       help="Filter by task method")
     parser.add_option("--owner", type="str", action="store",
                       help="Filter by task owner (name/ID)")
-    parser.add_option("--from", type="float", action="store", dest="from_ts",
+    parser.add_option("--from", "--after", type="time", action="store", dest="from_ts",
                       help="Logs from given timestamp")
-    parser.add_option("--to", type="float", action="store", dest="to_ts",
+    parser.add_option("--to", "--before", type="time", action="store", dest="to_ts",
                       help="Logs until given timestamp (included)")
     parser.add_option("--limit", action="store", type=int, default=None,
                       help="Limit data to last N items. [default: %default]")

