diff --git a/gplugin/gplugin-core.h b/gplugin/gplugin-core.h
--- a/gplugin/gplugin-core.h
+++ b/gplugin/gplugin-core.h
@@ -31,6 +31,7 @@
 typedef enum /*< flags,prefix=GPLUGIN_CORE_FLAGS,underscore_name=GPLUGIN_CORE >*/ {
 	GPLUGIN_CORE_FLAGS_NONE = 0,
 	GPLUGIN_CORE_FLAGS_DISABLE_NATIVE_LOADER = 1 << 0,
+	GPLUGIN_CORE_FLAGS_LOG_PLUGIN_STATE_CHANGES = 1 << 1,
 } GPluginCoreFlags;
 /* clang-format on */
 
@@ -39,6 +40,8 @@
 void gplugin_init(GPluginCoreFlags flags);
 void gplugin_uninit(void);
 
+GPluginCoreFlags gplugin_get_flags(void);
+
 G_END_DECLS
 
 #endif /* GPLUGIN_CORE_H */
# HG changeset patch
# Node ID 11416f210883
# Parent  683a9c8cda20
diff --git a/gplugin/gplugin-core.c b/gplugin/gplugin-core.c
--- a/gplugin/gplugin-core.c
+++ b/gplugin/gplugin-core.c
@@ -35,12 +35,16 @@
  * GPluginCoreFlags:
  * @GPLUGIN_CORE_FLAGS_NONE: No flags.
  * @GPLUGIN_CORE_FLAGS_DISABLE_NATIVE_LOADER: Disable the native plugin loader.
+ * @GPLUGIN_CORE_FLAGS_LOG_PLUGIN_STATE_CHANGES: Log plugin state changes with
+ *                                               g_message. Since: 0.34.0
  *
  * Flags to configure behaviors in GPlugin.
  *
  * Since: 0.31.0
  */
 
+static GPluginCoreFlags core_flags = 0;
+
 /******************************************************************************
  * API
  *****************************************************************************/
@@ -77,6 +81,8 @@
 	bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
 	bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
 
+	core_flags = flags;
+
 	if(flags & GPLUGIN_CORE_FLAGS_DISABLE_NATIVE_LOADER) {
 		register_native_loader = FALSE;
 	}
@@ -94,3 +100,18 @@
 {
 	gplugin_manager_private_uninit();
 }
+
+/**
+ * gplugin_core_get_flags:
+ *
+ * Gets the #GPluginCoreFlags that were passed to gplugin_init().
+ *
+ * Returns: The #GPluginCoreFlags that GPlugin was initialized with.
+ *
+ * Since: 0.34.0
+ */
+GPluginCoreFlags
+gplugin_get_flags(void)
+{
+	return core_flags;
+}
diff --git a/gplugin/gplugin-plugin.h b/gplugin/gplugin-plugin.h
--- a/gplugin/gplugin-plugin.h
+++ b/gplugin/gplugin-plugin.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
+ * Copyright (C) 2011-2021 Gary Kramlich <grim@reaperworld.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -72,6 +72,8 @@
 
 GError *gplugin_plugin_get_error(GPluginPlugin *plugin);
 
+const gchar *gplugin_plugin_state_to_string(GPluginPluginState state);
+
 G_END_DECLS
 
 #endif /* GPLUGIN_PLUGIN_H */
diff --git a/gplugin/gplugin-plugin.c b/gplugin/gplugin-plugin.c
--- a/gplugin/gplugin-plugin.c
+++ b/gplugin/gplugin-plugin.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011-2020 Gary Kramlich <grim@reaperworld.com>
+ * Copyright (C) 2011-2021 Gary Kramlich <grim@reaperworld.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -15,8 +15,10 @@
  * License along with this library; if not, see <https://www.gnu.org/licenses/>.
  */
 
+#include <gplugin/gplugin-plugin.h>
+
+#include <gplugin/gplugin-core.h>
 #include <gplugin/gplugin-enums.h>
-#include <gplugin/gplugin-plugin.h>
 #include <gplugin/gplugin-private.h>
 
 /**
@@ -278,6 +280,19 @@
 
 	g_object_set(G_OBJECT(plugin), "state", state, NULL);
 
+	if(gplugin_get_flags() & GPLUGIN_CORE_FLAGS_LOG_PLUGIN_STATE_CHANGES) {
+		GPluginPluginInfo *info = gplugin_plugin_get_info(plugin);
+
+		g_message(
+			"plugin %s state changed from %s to %s: filename=%s",
+			gplugin_plugin_info_get_id(info),
+			gplugin_plugin_state_to_string(oldstate),
+			gplugin_plugin_state_to_string(state),
+			gplugin_plugin_get_filename(plugin));
+
+		g_clear_object(&info);
+	}
+
 	g_signal_emit(plugin, signals[SIG_STATE_CHANGED], 0, oldstate, state);
 }
 
@@ -301,3 +316,42 @@
 
 	return error;
 }
+
+/**
+ * gplugin_plugin_state_to_string:
+ * @state: The #GPluginPluginState.
+ *
+ * Gets a string representation of @state.
+ *
+ * Returns: The string representation of @state.
+ *
+ * Since: 0.32.0
+ */
+const gchar *
+gplugin_plugin_state_to_string(GPluginPluginState state)
+{
+	const gchar *state_str = NULL;
+
+	switch(state) {
+		case GPLUGIN_PLUGIN_STATE_QUERIED:
+			state_str = "queried";
+			break;
+		case GPLUGIN_PLUGIN_STATE_REQUERY:
+			state_str = "requery";
+			break;
+		case GPLUGIN_PLUGIN_STATE_LOADED:
+			state_str = "loaded";
+			break;
+		case GPLUGIN_PLUGIN_STATE_LOAD_FAILED:
+			state_str = "load-failed";
+			break;
+		case GPLUGIN_PLUGIN_STATE_UNLOAD_FAILED:
+			state_str = "unload-failed";
+			break;
+		default:
+			state_str = "unknown";
+			break;
+	}
+
+	return state_str;
+}
