# Writing a custom Python plugin

**URL:** <https://community.netdata.cloud/t/writing-a-custom-python-plugin/869>\
**Category:** Help\
**Tags:** agent\
**Created:** [February 3, 2021, 6:51pm UTC](https://community.netdata.cloud/t/writing-a-custom-python-plugin/869 "2021-02-03T18:51:40Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![Skykeeper](https://avatars.discourse-cdn.com/v4/letter/s/b5ac83/32.png) [@Skykeeper](https://community.netdata.cloud/u/Skykeeper)\
**Post date:** [February 3, 2021, 6:51pm UTC](https://community.netdata.cloud/t/writing-a-custom-python-plugin/869/1 "2021-02-03T18:51:40Z")

</div>

Hi,

I have been developing a custom plugin to collect monitoring data from one of our application to netdata.

The conceptual issue is the number of dimensions in some of the charts are dynamic. The examples and plugins that are based on the Framework classes (e.g. SimpleService, URLService, etc) all appear to have a static chart and dimensions definition, and to output it once when the plugin is started.

The page “External Plugin API” states that any program that can output on standard output can be a module ([External plugins overview | Learn Netdata](https://learn.netdata.cloud/docs/agent/collectors/plugins.d#external-plugins-api)). Great, so I did just this, following up on an old example from a github thread ([Struggling writing custom plugins · Issue #206 · netdata/netdata · GitHub](https://github.com/netdata/netdata/issues/206)), and following up the general meta-language algorithm given in the documentation.

Great thing: it works, and I have the charts as I want them.  
Bad thing: when I put my plugin in the /usr/libexec/netdata/python.d, all the other python plugins don’t run, as if my plugin was hijacking the internal scheduling.

Questions:

1. is there any signal/character/command that I need to output on the standard output that tells netdata “I am done for this iteration”
2. what is the proper way to force reoutput of CHART/DIMENSION when using the framework classes such as SimpleService?

Many thanks for your help,  
Quentin

---

<div class="post-metadata">

**Author:** ![ilyam8](https://yyz1.discourse-cdn.com/flex029/user_avatar/community.netdata.cloud/ilyam8/32/134_2.png) [@ilyam8](https://community.netdata.cloud/u/ilyam8)\
**Post date:** [February 3, 2021, 10:35pm UTC](https://community.netdata.cloud/t/writing-a-custom-python-plugin/869/2 "2021-02-03T22:35:38Z")

</div>

> [@Skykeeper](#):
>
> Bad thing: when I put my plugin in the /usr/libexec/netdata/python.d, all the other python plugins don’t run, as if my plugin was hijacking the internal scheduling.

Hi, lets start from the bad thing.

- python.d.plugin debug mode

Debug mode is nice, easy to spot the problem, very handy during developing

```cmd
# as netdata user

# debug specific module
./python.d.plugin debug trace module_name

# debug all modules
./python.d.plugin debug

```

- error.log

If there is something doesn’t work `error.log` is a good place to check

`python.d.plugin` doesn’t work?

```auto
grep python.d.plugin error.log
grep my_python_module error.log

```

---

<div class="post-metadata">

**Author:** ![Skykeeper](https://avatars.discourse-cdn.com/v4/letter/s/b5ac83/32.png) [@Skykeeper](https://community.netdata.cloud/u/Skykeeper)\
**Post date:** [February 3, 2021, 11:25pm UTC](https://community.netdata.cloud/t/writing-a-custom-python-plugin/869/3 "2021-02-03T23:25:14Z")

</div>

Hi,

Thank you for your kind reply.

Yes, I forgot to mention that I did run in debug mode. It runs fine; no error show up, the expected set of CHART/DIMENSION/BEGIN/SET/END commands are issued. Actually, the resulting charts are OK and perfect in every way; except from the fact that I can’t seem to run more than just my own plugin.

When debugging all modules, it correctly shows the debug info for reading the configuration file, and “built XX job config”. Then when it comes to the custom plugin, just prints the stdout of the module. I paste here a cropped version that shows the idea (to avoid hundreds of lines ;):  
I have left two modules for which I have an explicit configuration: postgres and weblog. The log progresses nicely in alphabetical order through the modules, until it reaches mine:

> ubuntu@monitor:/usr/libexec/netdata/python.d$ sudo -u netdata ./…/plugins.d/python.d.plugin debug  
> 2021-02-03 23:10:41: python.d INFO: plugin[main] : using python v3  
> 2021-02-03 23:10:41: python.d DEBUG: plugin[main] : looking for ‘python.d.conf’ in [‘/etc/netdata’, ‘/usr/lib/netdata/conf.d’]  
> 2021-02-03 23:10:41: python.d DEBUG: plugin[main] : loading ‘/usr/lib/netdata/conf.d/python.d.conf’  
> 2021-02-03 23:10:41: python.d DEBUG: plugin[main] : ‘/usr/lib/netdata/conf.d/python.d.conf’ is loaded  
> 2021-02-03 23:10:41: python.d DEBUG: plugin[main] : looking for ‘pythond-jobs-statuses.json’ in /var/lib/netdata  
> 2021-02-03 23:10:41: python.d DEBUG: plugin[main] : loading ‘/var/lib/netdata/pythond-jobs-statuses.json’  
> 2021-02-03 23:10:41: python.d DEBUG: plugin[main] : ‘/var/lib/netdata/pythond-jobs-statuses.json’ is loaded  
> […]  
> 2021-02-03 23:10:41: python.d DEBUG: plugin[main] : [postgres] looking for ‘postgres.conf’ in [‘/etc/netdata/python.d’, ‘/usr/lib/netdata/conf.d/python.d’]  
> 2021-02-03 23:10:41: python.d DEBUG: plugin[main] : [postgres] loading ‘/etc/netdata/python.d/postgres.conf’  
> 2021-02-03 23:10:41: python.d DEBUG: plugin[main] : [postgres] ‘/etc/netdata/python.d/postgres.conf’ is loaded  
> 2021-02-03 23:10:41: python.d INFO: plugin[main] : [postgres] built 1 job(s) configs  
> 2021-02-03 23:10:41: python.d DEBUG: plugin[main] : postgres[tcp] was previously active, applying recovering settings  
> […]  
> 2021-02-03 23:10:42: python.d DEBUG: plugin[main] : [web\_log] looking for ‘web\_log.conf’ in [‘/etc/netdata/python.d’, ‘/usr/lib/netdata/conf.d/python.d’]  
> 2021-02-03 23:10:42: python.d DEBUG: plugin[main] : [web\_log] loading ‘/usr/lib/netdata/conf.d/python.d/web\_log.conf’  
> 2021-02-03 23:10:42: python.d DEBUG: plugin[main] : [web\_log] ‘/usr/lib/netdata/conf.d/python.d/web\_log.conf’ is loaded  
> 2021-02-03 23:10:42: python.d INFO: plugin[main] : [web\_log] built 13 job(s) configs  
> […]  
> CHART wmspanel\_nimble.incoming\_streams\_RTMP ‘’ ‘Nimble incoming stream status (RTMP)’ bool RTMP wmspanel\_nimble\_incoming\_stream stacked 10000 60 ‘’ ‘python.d.plugin’ ‘nimble’  
> DIMENSION ‘hls/23\_test-mmc\_lqubtp9\_vps252383’ ‘hls/23\_test-mmc\_lqubtp9\_vps252383’ absolute 1 1 ’ ’  
> […]  
> BEGIN wmspanel\_nimble.incoming\_streams\_RTMP 0  
> SET “hls/23\_test-mmc\_lqubtp9\_vps252383” = 1  
> END

and then waits for the next update (1 minute) to reprint the next set of values.

If I run debug on an official module, e.g. postgres, it behaves similarly, but afterwards there is a

> python.d DEBUG: postgres[tcp] : update =\> [OK] (elapsed time: 28, failed retries in a row: 0)

once the iteration is done, which I don’t understand how to trigger?

Many thanks for your feedback,  
Quentin

---

<div class="post-metadata">

**Author:** ![ilyam8](https://yyz1.discourse-cdn.com/flex029/user_avatar/community.netdata.cloud/ilyam8/32/134_2.png) [@ilyam8](https://community.netdata.cloud/u/ilyam8)\
**Post date:** [February 4, 2021, 12:00pm UTC](https://community.netdata.cloud/t/writing-a-custom-python-plugin/869/4 "2021-02-04T12:00:14Z")

</div>

> The log progresses nicely in alphabetical order through the modules, until it reaches mine

lets see your module debug output

```cmd
./python.d.plugin debug trace wmspanel # or wmspanel_nimble, not sure what the name is

```

---

<div class="post-metadata">

**Author:** ![Skykeeper](https://avatars.discourse-cdn.com/v4/letter/s/b5ac83/32.png) [@Skykeeper](https://community.netdata.cloud/u/Skykeeper)\
**Post date:** [February 4, 2021, 1:22pm UTC](https://community.netdata.cloud/t/writing-a-custom-python-plugin/869/5 "2021-02-04T13:22:39Z")

</div>

Sure, here is the log for two full iterations:

> q@monitor:/usr/libexec/netdata/plugins.d$ sudo -u netdata ./python.d.plugin debug trace wmspanel\_nimble  
> 2021-02-04 13:31:13: python.d INFO: plugin[main] : using python v3  
> 2021-02-04 13:31:13: python.d DEBUG: plugin[main] : looking for ‘python.d.conf’ in [‘/etc/netdata’, ‘/usr/lib/netdata/conf.d’]  
> 2021-02-04 13:31:13: python.d DEBUG: plugin[main] : loading ‘/usr/lib/netdata/conf.d/python.d.conf’  
> 2021-02-04 13:31:13: python.d DEBUG: plugin[main] : ‘/usr/lib/netdata/conf.d/python.d.conf’ is loaded  
> 2021-02-04 13:31:13: python.d DEBUG: plugin[main] : looking for ‘pythond-jobs-statuses.json’ in /var/lib/netdata  
> 2021-02-04 13:31:13: python.d DEBUG: plugin[main] : loading ‘/var/lib/netdata/pythond-jobs-statuses.json’  
> 2021-02-04 13:31:13: python.d DEBUG: plugin[main] : ‘/var/lib/netdata/pythond-jobs-statuses.json’ is loaded  
> CHART wmspanel\_nimble.incoming\_streams\_MPEGTS ‘’ ‘Nimble incoming stream status (MPEGTS)’ bool MPEGTS wmspanel\_nimble\_incoming\_stream stacked 10000 60 ‘’ ‘python.d.plugin’ ‘nimble’  
> DIMENSION ‘stream\_0’ ‘stream\_0’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_1’ ‘stream\_1’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_10’ ‘stream\_10’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_11’ ‘stream\_11’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_12’ ‘stream\_12’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_13’ ‘stream\_13’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_14’ ‘stream\_14’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_15’ ‘stream\_15’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_16’ ‘stream\_16’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_17’ ‘stream\_17’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_19’ ‘stream\_19’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_2’ ‘stream\_2’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_20’ ‘stream\_20’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_21’ ‘stream\_21’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_22’ ‘stream\_22’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_23’ ‘stream\_23’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_24’ ‘stream\_24’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_25’ ‘stream\_25’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_26’ ‘stream\_26’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_27’ ‘stream\_27’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_28’ ‘stream\_28’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_3’ ‘stream\_3’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_5’ ‘stream\_5’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_8’ ‘stream\_8’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_9’ ‘stream\_9’ absolute 1 1 ’ ’  
> CHART wmspanel\_nimble.incoming\_streams\_RTMP ‘’ ‘Nimble incoming stream status (RTMP)’ bool RTMP wmspanel\_nimble\_incoming\_stream stacked 10000 60 ‘’ ‘python.d.plugin’ ‘nimble’  
> DIMENSION ‘stream\_18’ ‘stream\_18’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_4’ ‘stream\_4’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_7’ ‘stream\_7’ absolute 1 1 ’ ’  
> CHART wmspanel\_nimble.incoming\_streams\_ICECAST ‘’ ‘Nimble incoming stream status (ICECAST)’ bool ICECAST wmspanel\_nimble\_incoming\_stream stacked 10000 60 ‘’ ‘python.d.plugin’ ‘nimble’  
> DIMENSION ‘stream\_6’ ‘stream\_6’ absolute 1 1 ’ ’  
> BEGIN wmspanel\_nimble.incoming\_streams\_MPEGTS 0  
> SET “stream\_0” = 1  
> SET “stream\_1” = 1  
> SET “stream\_10” = 1  
> SET “stream\_11” = 1  
> SET “stream\_12” = 1  
> SET “stream\_13” = 0  
> SET “stream\_14” = 0  
> SET “stream\_15” = 0  
> SET “stream\_16” = 1  
> SET “stream\_17” = 1  
> SET “stream\_19” = 1  
> SET “stream\_2” = 1  
> SET “stream\_20” = 1  
> SET “stream\_21” = 1  
> SET “stream\_22” = 1  
> SET “stream\_23” = 1  
> SET “stream\_24” = 1  
> SET “stream\_25” = 1  
> SET “stream\_26” = 1  
> SET “stream\_27” = 1  
> SET “stream\_28” = 1  
> SET “stream\_3” = 1  
> SET “stream\_5” = 1  
> SET “stream\_8” = 1  
> SET “stream\_9” = 1  
> END  
> BEGIN wmspanel\_nimble.incoming\_streams\_RTMP 0  
> SET “stream\_18” = 1  
> SET “stream\_4” = 1  
> SET “stream\_7” = 1  
> END  
> BEGIN wmspanel\_nimble.incoming\_streams\_ICECAST 0  
> SET “stream\_6” = 1  
> END

1 minute wait for the next collection (log stays the same, nothing moves or anything), then:

> CHART wmspanel\_nimble.incoming\_streams\_MPEGTS ‘’ ‘Nimble incoming stream status (MPEGTS)’ bool MPEGTS wmspanel\_nimble\_incoming\_stream stacked 10000 60 ‘’ ‘python.d.plugin’ ‘nimble’  
> DIMENSION ‘stream\_0’ ‘stream\_0’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_1’ ‘stream\_1’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_10’ ‘stream\_10’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_11’ ‘stream\_11’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_12’ ‘stream\_12’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_13’ ‘stream\_13’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_14’ ‘stream\_14’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_15’ ‘stream\_15’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_16’ ‘stream\_16’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_17’ ‘stream\_17’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_19’ ‘stream\_19’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_2’ ‘stream\_2’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_20’ ‘stream\_20’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_21’ ‘stream\_21’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_22’ ‘stream\_22’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_23’ ‘stream\_23’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_24’ ‘stream\_24’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_25’ ‘stream\_25’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_26’ ‘stream\_26’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_27’ ‘stream\_27’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_28’ ‘stream\_28’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_3’ ‘stream\_3’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_5’ ‘stream\_5’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_8’ ‘stream\_8’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_9’ ‘stream\_9’ absolute 1 1 ’ ’  
> CHART wmspanel\_nimble.incoming\_streams\_RTMP ‘’ ‘Nimble incoming stream status (RTMP)’ bool RTMP wmspanel\_nimble\_incoming\_stream stacked 10000 60 ‘’ ‘python.d.plugin’ ‘nimble’  
> DIMENSION ‘stream\_18’ ‘stream\_18’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_4’ ‘stream\_4’ absolute 1 1 ’ ’  
> DIMENSION ‘stream\_7’ ‘stream\_7’ absolute 1 1 ’ ’  
> CHART wmspanel\_nimble.incoming\_streams\_ICECAST ‘’ ‘Nimble incoming stream status (ICECAST)’ bool ICECAST wmspanel\_nimble\_incoming\_stream stacked 10000 60 ‘’ ‘python.d.plugin’ ‘nimble’  
> DIMENSION ‘stream\_6’ ‘stream\_6’ absolute 1 1 ’ ’  
> BEGIN wmspanel\_nimble.incoming\_streams\_MPEGTS 60059  
> SET “stream\_0” = 1  
> SET “stream\_1” = 1  
> SET “stream\_10” = 1  
> SET “stream\_11” = 1  
> SET “stream\_12” = 1  
> SET “stream\_13” = 0  
> SET “stream\_14” = 0  
> SET “stream\_15” = 0  
> SET “stream\_16” = 1  
> SET “stream\_17” = 1  
> SET “stream\_19” = 1  
> SET “stream\_2” = 1  
> SET “stream\_20” = 1  
> SET “stream\_21” = 1  
> SET “stream\_22” = 1  
> SET “stream\_23” = 1  
> SET “stream\_24” = 1  
> SET “stream\_25” = 1  
> SET “stream\_26” = 1  
> SET “stream\_27” = 1  
> SET “stream\_28” = 1  
> SET “stream\_3” = 1  
> SET “stream\_5” = 1  
> SET “stream\_8” = 1  
> SET “stream\_9” = 1  
> END  
> BEGIN wmspanel\_nimble.incoming\_streams\_RTMP 60059  
> SET “stream\_18” = 1  
> SET “stream\_4” = 1  
> SET “stream\_7” = 1  
> END  
> BEGIN wmspanel\_nimble.incoming\_streams\_ICECAST 60059  
> SET “stream\_6” = 1  
> END  
> ^C2021-02-04 13:32:18: python.d INFO: plugin[main] : exiting from main…

CTRL+C’d

(EDIT: copy/paste error)

---

<div class="post-metadata">

**Author:** ![ilyam8](https://yyz1.discourse-cdn.com/flex029/user_avatar/community.netdata.cloud/ilyam8/32/134_2.png) [@ilyam8](https://community.netdata.cloud/u/ilyam8)\
**Post date:** [February 4, 2021, 5:14pm UTC](https://community.netdata.cloud/t/writing-a-custom-python-plugin/869/6 "2021-02-04T17:14:42Z")

</div>

> [@Skykeeper](#):
>
> If I run debug on an official module, e.g. postgres, it behaves similarly, but afterwards there is a
> 
> > python.d DEBUG: postgres[tcp] : update =\> [OK] (elapsed time: 28, failed retries in a row: 0)

it is debug level message, only seen if you add `debug` cli option

* * *

> and then waits for the next update (1 minute) to reprint the next set of values.

unlikely, but… it is possible you add some sleep function? 🤔

---

<div class="post-metadata">

**Author:** ![Skykeeper](https://avatars.discourse-cdn.com/v4/letter/s/b5ac83/32.png) [@Skykeeper](https://community.netdata.cloud/u/Skykeeper)\
**Post date:** [February 4, 2021, 6:41pm UTC](https://community.netdata.cloud/t/writing-a-custom-python-plugin/869/7 "2021-02-04T18:41:51Z")

</div>

I am not sure what you mean…?

Yes there is a call to time.sleep(), as recommended from the pseudo-code at [External plugins overview | Learn Netdata](https://learn.netdata.cloud/docs/agent/collectors/plugins.d#writing-plugins-properly) (bottom)

The plugin code looks like this:

```
def main_loop():
    # internal defaults for the command line arguments                                                                                                                                                      
    update_every = 60 # seconds                                                                                                                                                                             
    update_every *= 1000
    get_millis = lambda: int(round(time.time() * 1000))
   
    count = 0
    last_run = next_run = now = get_millis()
    while True:
        if next_run <= now:
            count += 1
            now = get_millis()
            while next_run <= now:
               next_run += update_every
            dt = now - last_run
            last_run = now
            # on the first iteration, don't set dt allowing netdata to align itself                                                                                                                                                              
            if count == 1:
                dt = 0
          
            status = nimble_get_incoming_streams()
            netdata_generate_charts(status)
            netdata_generate_datapoints(status, dt)

            time.sleep(update_every / 1000 / 10)                                                                                                                                                               
            now = get_millis()

```

---

<div class="post-metadata">

**Author:** ![ilyam8](https://yyz1.discourse-cdn.com/flex029/user_avatar/community.netdata.cloud/ilyam8/32/134_2.png) [@ilyam8](https://community.netdata.cloud/u/ilyam8)\
**Post date:** [February 4, 2021, 9:49pm UTC](https://community.netdata.cloud/t/writing-a-custom-python-plugin/869/8 "2021-02-04T21:49:47Z")

</div>

> [@Skykeeper](#):
>
> Yes there is a call to time.sleep()

That is the problem, you only need to implement data collection logic, python.d.plugin executes `_get_data` method every `update_every`

---

<div class="post-metadata">

**Author:** ![Skykeeper](https://avatars.discourse-cdn.com/v4/letter/s/b5ac83/32.png) [@Skykeeper](https://community.netdata.cloud/u/Skykeeper)\
**Post date:** [February 8, 2021, 10:50am UTC](https://community.netdata.cloud/t/writing-a-custom-python-plugin/869/9 "2021-02-08T10:50:12Z")

</div>

Yes… to go back to the beginning of the thread, I attempted to approach things this way because the number of dimensions in my charts may be dynamic (e.g. when a new stream shows up)

The examples of plugin implementation use a static array for storing charts/dimensions which are only transmitted to netdata once on plugin startup.

If you know how to force the refresh of dimensions, I’d be happy to move my collector to a child of the SimpleService class 🙂

At the same time, I do believe I followed the documentation to the letter, so it is a bit misleading…

---

<div class="post-metadata">

**Author:** ![Skykeeper](https://avatars.discourse-cdn.com/v4/letter/s/b5ac83/32.png) [@Skykeeper](https://community.netdata.cloud/u/Skykeeper)\
**Post date:** [February 9, 2021, 5:51pm UTC](https://community.netdata.cloud/t/writing-a-custom-python-plugin/869/10 "2021-02-09T17:51:15Z")

</div>

Hi all,

I come to resolve my own issue and share if anyone has the same issue of:

1. Having a dynamic number of charts
2. Having dynamic dimensions inside those charts.

The way I got it to work is to implement a child of SimpleService.  
Then, in the get\_data() function:

1. Redefine`self.order = []`, and `self.definitions = { }`
2. Clear `self.charts`: you need to remake the call that is done (for you) in the SimpleService constructor, i.e. `self.charts = Charts(job_name=self.actual_name, priority=configuration["priority"], cleanup=configuration["chart_cleanup"], get_update_every=self.get_update_every, module_name=self.module_name)`. You will need to have deep copied the configuration OrderedDict that netdata has given you in your own class constructor.
3. Fill in your new self.order and self.definitions, based on the templates, dynamically constructed from the data that you have gathered.
4. Explicitly call `SimpleService.create(self)` from within `get_data()`. This will regenerate the chart content and transmit the commands to the backend.

If any netdata team member is watching this thread, I would like to kindly point out that:

1. the claim that “Any program that can print a few values to its standard output can become a Netdata external plugin” is a bit misleading as the plugin scheduling basically makes this impossible to just output data and expect it to work alongside other things (on this page → [External plugins overview | Learn Netdata](https://learn.netdata.cloud/docs/agent/collectors/plugins.d) )
2. I have already stated in another thread there is next to no documentation in the source code… I don’t know if you have an internal codebase with comments that you just strip out for release… or if you just remember everything… but it makes it unnecessarily complex to figure out the inner workings…

Many thanks  
Quentin

---

<div class="post-metadata">

**Author:** ![OdysLam](https://yyz1.discourse-cdn.com/flex029/user_avatar/community.netdata.cloud/odyslam/32/98_2.png) [@OdysLam](https://community.netdata.cloud/u/OdysLam)\
**Post date:** [February 9, 2021, 8:20pm UTC](https://community.netdata.cloud/t/writing-a-custom-python-plugin/869/11 "2021-02-09T20:20:13Z")

</div>

@Skykeeper

This is invaluable feedback. I can’t thank you enough! We are actually working on a python collector guide, so your feedback will probably be incorporated into the guide, so you are actively contributing in making this easier for everyone ✌.

If you are interested, I would love your feedback ahead of Guide’s release. Pinging our documentation guru for extra visibility @joel .

Many thanks and I will come back here 🙂

---

<div class="post-metadata">

**Author:** ![Skykeeper](https://avatars.discourse-cdn.com/v4/letter/s/b5ac83/32.png) [@Skykeeper](https://community.netdata.cloud/u/Skykeeper)\
**Post date:** [February 10, 2021, 11:38am UTC](https://community.netdata.cloud/t/writing-a-custom-python-plugin/869/12 "2021-02-10T11:38:38Z")

</div>

@OdysLam

I am happy to help if you think my contribution can be worthwhile.  
Do not hesitate to ping me or email through the registered address.

Cheers

---

<div class="post-metadata">

**Author:** ![andrewm4894](https://yyz1.discourse-cdn.com/flex029/user_avatar/community.netdata.cloud/andrewm4894/32/125_2.png) [@andrewm4894](https://community.netdata.cloud/u/andrewm4894)\
**Post date:** [February 12, 2021, 4:12pm UTC](https://community.netdata.cloud/t/writing-a-custom-python-plugin/869/13 "2021-02-12T16:12:31Z")

</div>

Just chiming in in case others find useful - i think the alarms collector also has a sort of example of dynamically creating charts and/or dims on the fly as needed.

> <https://github.com/netdata/netdata/blob/master/collectors/python.d.plugin/alarms/alarms.chart.py>

Using the `add_dimension()` and `del_dimension()` methods of `self.charts`

For adding charts dynamically i have also had a need to do this recently and there is an example of how i got it to work for me here: [https://github.com/andrewm4894/netdata/blob/aggregator-collector/collectors/python.d.plugin/aggregator/aggregator.chart.py#L46](https://github.com/andrewm4894/netdata/blob/aggregator-collector/collectors/python.d.plugin/aggregator/aggregator.chart.py#L46)

Basically use the `add_chart()` method if needed.

I’m not sure if thats quite the same as the use cases needed here but felt like it was maybe close enough to share some more info in case anyone in future finds it useful.
