Repository: weaveworks/grafanalib

Issue:
Support Logs panel type
## What you expected to happen?
We should support the Logs panel type.

## What happened?
Not supported as of this writing

Discussion:

Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 9a39d3c..808dfe3 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -5,6 +5,7 @@ Changelog
 0.x.x (TBD)
 ===========
 
+* Added Logs panel (https://grafana.com/docs/grafana/latest/panels/visualizations/logs-panel/)
 * ...
 
 Changes
diff --git a/grafanalib/core.py b/grafanalib/core.py
index e9996fe..4957936 100644
--- a/grafanalib/core.py
+++ b/grafanalib/core.py
@@ -83,6 +83,7 @@ ALERTLIST_TYPE = 'alertlist'
 BARGAUGE_TYPE = 'bargauge'
 GAUGE_TYPE = 'gauge'
 DASHBOARDLIST_TYPE = 'dashlist'
+LOGS_TYPE = 'logs'
 HEATMAP_TYPE = 'heatmap'
 STATUSMAP_TYPE = 'flant-statusmap-panel'
 SVG_TYPE = 'marcuscalidus-svg-panel'
@@ -2497,7 +2498,6 @@ class DashboardList(Panel):
     def to_json_data(self):
         return self.panel_json(
             {
-                'height': self.height,
                 'fieldConfig': {
                     'defaults': {
                         'custom': {},
@@ -2516,6 +2516,49 @@ class DashboardList(Panel):
         )
 
 
+@attr.s
+class Logs(Panel):
+    """Generates Logs panel json structure
+    Grafana doc on Logs panel: https://grafana.com/docs/grafana/latest/panels/visualizations/logs-panel/
+    :param title: panel title
+    :param description: optional panel description
+    :param editable: defines if panel is editable via web interfaces
+    :param height: defines panel height
+    :param id: panel id
+    :param links: additional web links
+    :param span: defines the number of spans that will be used for panel
+    :param transparent: defines if the panel is transparent
+
+    :param showLabels: Show or hide the unique labels column, which shows only non-common labels
+    :param showTime: Show or hide the log timestamp column
+    :param wrapLogMessages: Toggle line wrapping
+    :param sortOrder: Display results in 'Descending' or 'Ascending' time order. The default is Descending, showing the newest logs first.
+    """
+    showLabels = attr.ib(default=False, validator=instance_of(bool))
+    showTime = attr.ib(default=False, validator=instance_of(bool))
+    wrapLogMessages = attr.ib(default=False, validator=instance_of(bool))
+    sortOrder = attr.ib(default='Descending', validator=instance_of(str))
+
+    def to_json_data(self):
+        return self.panel_json(
+            {
+                'fieldConfig': {
+                    'defaults': {
+                        'custom': {},
+                    },
+                    'overrides': []
+                },
+                'options': {
+                    'showLabels': self.showLabels,
+                    'showTime': self.showTime,
+                    'sortOrder': self.sortOrder,
+                    'wrapLogMessage': self.wrapLogMessages
+                },
+                'type': LOGS_TYPE,
+            }
+        )
+
+
 @attr.s
 class Threshold(object):
     """Threshold for a gauge


Test patch:
diff --git a/grafanalib/tests/test_core.py b/grafanalib/tests/test_core.py
index d85e3da..cc8225b 100644
--- a/grafanalib/tests/test_core.py
+++ b/grafanalib/tests/test_core.py
@@ -131,3 +131,18 @@ def test_dashboard_list():
     assert data['datasource'] is None
     assert data['title'] == title
     assert data['starred'] is True
+
+
+def test_logs_panel():
+    data_source = 'dummy data source'
+    targets = ['dummy_prom_query']
+    title = 'dummy title'
+    logs = G.Logs(data_source, targets, title)
+    data = logs.to_json_data()
+    assert data['targets'] == targets
+    assert data['datasource'] == data_source
+    assert data['title'] == title
+    assert data['options']['showLabels'] is False
+    assert data['options']['showTime'] is False
+    assert data['options']['wrapLogMessage'] is False
+    assert data['options']['sortOrder'] == 'Descending'