[tracker-miners/wip/carlosg/cli-improvements: 13/30] tracker: Add reporting of logged errors to "tracker status"




commit 062123b9f42ab5d0a8232132cbc28d8bb48229cf
Author: Carlos Garnacho <carlosg gnome org>
Date:   Mon Aug 17 01:12:20 2020 +0200

    tracker: Add reporting of logged errors to "tracker status"
    
    The plain "tracker status" command will list all logged error reports,
    the "tracker status [pattern]" command can be used to match file paths,
    and print a more detailed error report.

 src/libtracker-miners-common/meson.build          |   1 +
 src/libtracker-miners-common/tracker-common.h     |   1 +
 src/libtracker-miners-common/tracker-term-utils.c |  89 ++++++++++
 src/libtracker-miners-common/tracker-term-utils.h |  39 +++++
 src/tracker/tracker-color.h                       |   3 +
 src/tracker/tracker-status.c                      | 194 +++++++++++++++++++++-
 6 files changed, 326 insertions(+), 1 deletion(-)
---
diff --git a/src/libtracker-miners-common/meson.build b/src/libtracker-miners-common/meson.build
index 7868f8623..06e91d870 100644
--- a/src/libtracker-miners-common/meson.build
+++ b/src/libtracker-miners-common/meson.build
@@ -17,6 +17,7 @@ tracker_miners_common_sources = [
   'tracker-ioprio.c',
   'tracker-language.c',
   'tracker-sched.c',
+  'tracker-term-utils.c',
   'tracker-type-utils.c',
   'tracker-utils.c',
   'tracker-locale.c',
diff --git a/src/libtracker-miners-common/tracker-common.h b/src/libtracker-miners-common/tracker-common.h
index 1d4f364c4..05f1c520e 100644
--- a/src/libtracker-miners-common/tracker-common.h
+++ b/src/libtracker-miners-common/tracker-common.h
@@ -40,6 +40,7 @@
 #include "tracker-language.h"
 #include "tracker-sched.h"
 #include "tracker-seccomp.h"
+#include "tracker-term-utils.h"
 #include "tracker-type-utils.h"
 #include "tracker-utils.h"
 #include "tracker-locale.h"
diff --git a/src/libtracker-miners-common/tracker-term-utils.c 
b/src/libtracker-miners-common/tracker-term-utils.c
new file mode 100644
index 000000000..9de157cf8
--- /dev/null
+++ b/src/libtracker-miners-common/tracker-term-utils.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2020, Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Author: Carlos Garnacho <carlosg gnome org>
+ */
+
+#include "tracker-term-utils.h"
+
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+static guint n_columns = 0;
+static guint n_rows = 0;
+
+gchar *
+tracker_term_ellipsize (const gchar          *str,
+                        gint                  max_len,
+                        TrackerEllipsizeMode  mode)
+{
+       gint len = strlen (str);
+       gchar *substr, *retval;
+
+       if (len < max_len)
+               return g_strdup (str);
+
+       if (mode == TRACKER_ELLIPSIZE_START) {
+               substr = g_memdup (str + len - max_len + 1, max_len - 1);
+               retval = g_strdup_printf ("…%s", substr);
+               g_free (substr);
+       } else {
+               substr = g_memdup (str, max_len - 1);
+               retval = g_strdup_printf ("%s…", substr);
+               g_free (substr);
+       }
+
+       return retval;
+}
+
+static gboolean
+fd_term_dimensions (gint  fd,
+                    gint *cols,
+                    gint *rows)
+{
+        struct winsize ws = {};
+
+        if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
+                return FALSE;
+
+        if (ws.ws_col <= 0 || ws.ws_row <= 0)
+                return FALSE;
+
+        *cols = ws.ws_col;
+        *rows = ws.ws_row;
+
+        return TRUE;
+}
+
+void
+tracker_term_dimensions (guint *columns,
+                         guint *rows)
+{
+       if (n_columns == 0 || n_rows == 0)
+               fd_term_dimensions (STDOUT_FILENO, &n_columns, &n_rows);
+
+       if (n_columns <= 0)
+               n_columns = 80;
+       if (n_rows <= 0)
+               n_rows = 24;
+
+       if (columns)
+               *columns = n_columns;
+       if (rows)
+               *rows = n_rows;
+}
diff --git a/src/libtracker-miners-common/tracker-term-utils.h 
b/src/libtracker-miners-common/tracker-term-utils.h
new file mode 100644
index 000000000..e572b454e
--- /dev/null
+++ b/src/libtracker-miners-common/tracker-term-utils.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020, Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Author: Carlos Garnacho <carlosg gnome org>
+ */
+
+#ifndef __TRACKER_TERM_UTILS_H__
+#define __TRACKER_TERM_UTILS_H__
+
+#include <glib.h>
+
+typedef enum {
+       TRACKER_ELLIPSIZE_START,
+       TRACKER_ELLIPSIZE_END,
+} TrackerEllipsizeMode;
+
+gchar * tracker_term_ellipsize (const gchar          *str,
+                                gint                  max_len,
+                                TrackerEllipsizeMode  mode);
+
+void tracker_term_dimensions (guint *columns,
+                              guint *lines);
+
+#endif /* __TRACKER_TERM_UTILS_H__ */
diff --git a/src/tracker/tracker-color.h b/src/tracker/tracker-color.h
index 53d730376..6a5874da1 100644
--- a/src/tracker/tracker-color.h
+++ b/src/tracker/tracker-color.h
@@ -32,4 +32,7 @@
 #define CRIT_BEGIN "\033[1;31m" /* Red */
 #define CRIT_END   "\033[0m"
 
+#define BOLD_BEGIN "\033[0;1;39m"
+#define BOLD_END   "\033[0m"
+
 #endif /* __TRACKER_COLOR_H__ */
diff --git a/src/tracker/tracker-status.c b/src/tracker/tracker-status.c
index 04f25b360..2b6e2d8ec 100644
--- a/src/tracker/tracker-status.c
+++ b/src/tracker/tracker-status.c
@@ -32,7 +32,14 @@
 #include <libtracker-miners-common/tracker-common.h>
 #include <libtracker-sparql/tracker-sparql.h>
 
+#include "tracker-term-utils.h"
 #include "tracker-miner-manager.h"
+#include "tracker-color.h"
+
+#define GROUP "Report"
+#define KEY_URI "Uri"
+#define KEY_MESSAGE "Message"
+#define KEY_SPARQL "Sparql"
 
 #define STATUS_OPTIONS_ENABLED()         \
        (show_stat || \
@@ -473,6 +480,126 @@ are_miners_finished (gint *max_remaining_time)
        return finished;
 }
 
+static gint
+sort_by_date (gconstpointer a,
+              gconstpointer b)
+{
+       GFileInfo *info_a = (GFileInfo *) a, *info_b = (GFileInfo *) b;
+       gint64 time_a, time_b;
+
+       time_a = g_file_info_get_attribute_uint64 (info_a, G_FILE_ATTRIBUTE_TIME_CREATED);
+       time_b = g_file_info_get_attribute_uint64 (info_b, G_FILE_ATTRIBUTE_TIME_CREATED);
+
+       if (time_a < time_b)
+               return -1;
+       else if (time_a > time_b)
+               return 1;
+       return 0;
+}
+
+static GList *
+get_error_keyfiles (void)
+{
+       GFile *file;
+       GFileEnumerator *enumerator;
+       GList *infos = NULL, *keyfiles = NULL, *l;
+       gchar *path;
+
+       path = g_build_filename (g_get_user_cache_dir (),
+                                "tracker3",
+                                "files",
+                                "errors",
+                                NULL);
+       file = g_file_new_for_path (path);
+       g_free (path);
+
+       enumerator = g_file_enumerate_children (file,
+                                               G_FILE_ATTRIBUTE_STANDARD_NAME ","
+                                               G_FILE_ATTRIBUTE_TIME_CHANGED,
+                                               G_FILE_QUERY_INFO_NONE,
+                                               NULL,
+                                               NULL);
+       while (TRUE) {
+               GFileInfo *info;
+
+               if (!g_file_enumerator_iterate (enumerator, &info, NULL, NULL, NULL))
+                       break;
+               if (!info)
+                       break;
+
+               infos = g_list_prepend (infos, g_object_ref (info));
+       }
+
+       infos = g_list_sort (infos, sort_by_date);
+
+       for (l = infos; l; l = l->next) {
+               GKeyFile *keyfile;
+               GFile *child;
+
+               child = g_file_get_child (file, g_file_info_get_name (l->data));
+               path = g_file_get_path (child);
+               keyfile = g_key_file_new ();
+               g_key_file_load_from_file (keyfile,
+                                          path, 0,
+                                          NULL);
+
+               keyfiles = g_list_prepend (keyfiles, keyfile);
+               g_object_unref (child);
+       }
+
+       g_object_unref (enumerator);
+       g_list_free_full (infos, g_object_unref);
+
+       return keyfiles;
+}
+
+static gint
+print_errors (GList *keyfiles)
+{
+       gint cols, col_len[2];
+       gchar *col_header1, *col_header2;
+       GList *l;
+
+       tracker_term_dimensions (&cols, NULL);
+       col_len[0] = cols / 2;
+       col_len[1] = cols / 2 - 1;
+
+       col_header1 = tracker_term_ellipsize (_("Path"), col_len[0], TRACKER_ELLIPSIZE_END);
+       col_header2 = tracker_term_ellipsize (_("Message"), col_len[1], TRACKER_ELLIPSIZE_END);
+
+       g_print (BOLD_BEGIN "%-*s %-*s" BOLD_END "\n",
+                col_len[0], col_header1,
+                col_len[1], col_header2);
+       g_free (col_header1);
+       g_free (col_header2);
+
+       for (l = keyfiles; l; l = l->next) {
+               GKeyFile *keyfile = l->data;
+               gchar *uri, *message, *path, *str1, *str2;
+               GFile *file;
+
+               uri = g_key_file_get_string (keyfile, GROUP, KEY_URI, NULL);
+               file = g_file_new_for_uri (uri);
+               path = g_file_get_path (file);
+               message = g_key_file_get_string (keyfile, GROUP, KEY_MESSAGE, NULL);
+               g_object_unref (file);
+
+               str1 = tracker_term_ellipsize (path, col_len[0], TRACKER_ELLIPSIZE_START);
+               str2 = tracker_term_ellipsize (message, col_len[1], TRACKER_ELLIPSIZE_END);
+
+               g_print ("%-*s %-*s\n",
+                        col_len[0], str1,
+                        col_len[1], str2);
+               g_free (uri);
+               g_free (path);
+               g_free (message);
+               g_free (str1);
+               g_free (str2);
+       }
+
+       return EXIT_SUCCESS;
+}
+
 static int
 get_no_args (void)
 {
@@ -482,6 +609,7 @@ get_no_args (void)
        gdouble remaining;
        gint remaining_time;
        gint files, folders;
+       GList *keyfiles;
 
        /* How many files / folders do we have? */
        if (get_file_and_folder_count (&files, &folders) != 0) {
@@ -531,7 +659,68 @@ get_no_args (void)
                g_print ("%s\n", _("All data miners are idle, indexing complete"));
        }
 
-       g_print ("\n\n");
+       keyfiles = get_error_keyfiles ();
+
+       if (keyfiles) {
+               g_print (g_dngettext (NULL,
+                                     "%d recorded failure",
+                                     "%d recorded failures",
+                                     g_list_length (keyfiles)),
+                        g_list_length (keyfiles));
+
+               g_print ("\n\n");
+               print_errors (keyfiles);
+               g_list_free_full (keyfiles, (GDestroyNotify) g_key_file_unref);
+       }
+
+       return EXIT_SUCCESS;
+}
+
+static int
+show_errors (gchar **terms)
+{
+       GList *keyfiles, *l;
+       GKeyFile *keyfile;
+       guint i;
+       gboolean found = FALSE;
+
+       keyfiles = get_error_keyfiles ();
+
+       for (i = 0; terms[i] != NULL; i++) {
+               for (l = keyfiles; l; l = l->next) {
+                       GFile *file;
+                       gchar *uri, *path;
+
+                       keyfile = l->data;
+                       uri = g_key_file_get_string (keyfile, GROUP, KEY_URI, NULL);
+                       file = g_file_new_for_uri (uri);
+                       path = g_file_get_path (file);
+
+                       if (strstr (path, terms[i])) {
+                               gchar *sparql = g_key_file_get_string (keyfile, GROUP, KEY_SPARQL, NULL);
+                               gchar *message = g_key_file_get_string (keyfile, GROUP, KEY_MESSAGE, NULL);
+
+                               found = TRUE;
+                               g_print (BOLD_BEGIN "URI:" BOLD_END " %s\n", uri);
+
+                               if (message)
+                                       g_print (BOLD_BEGIN "%s:" BOLD_END " %s\n", _("Message"), message);
+                               if (sparql)
+                                       g_print (BOLD_BEGIN "SPARQL:" BOLD_END " %s\n", sparql);
+                               g_print ("\n");
+
+                               g_free (sparql);
+                               g_free (message);
+                       }
+
+                       g_object_unref (file);
+                       g_free (uri);
+                       g_free (path);
+               }
+       }
+
+       if (!found)
+               g_print (BOLD_BEGIN "%s" BOLD_END "\n", _("No reports found"));
 
        return EXIT_SUCCESS;
 }
@@ -578,5 +767,8 @@ main (int argc, const char **argv)
                return status_run ();
        }
 
+       if (terms)
+               return show_errors (terms);
+
        return status_run_default ();
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]