 Okay, welcome. Well, not nice to see you. I don't see you, but welcome to have you here. I will talk about the art of logging and mostly about how to get helpful error reports and, well, how to use logging such a type to solve your problems. I have essentially three parts of this talk. I have no idea why it's great out. Well, strange things happened with Datig. I have three parts. I hope I can see the lower parts. I will talk about logging in general and where to use it and when to use it. Then I will talk about logging in, well, in Qt, the Qt logging framework and how it's integrated in KDE and then about, well, what can you do if you are looking at the full system, so full embedded system or full desktop system. These are different parts, so I hope I will cover everything. I think the two first are the most important ones. Shortly about me, my name is Andreas. You can find my Nick Kola at Matrix Channels and, well, I'm with KDE for about 10 years already, mostly in the EDU section and also in recent years, mostly in licensing and different areas. At my day job, I'm working on big agriculture vehicles. That means vehicles where you cannot that easily get logs from. That's kind of a motivation for this talk. Well, just looking if I can, unfortunately, no, I cannot blend it in. So why am I giving this talk? Sometimes you get bug reports on the Vaxilla and you want to get logs and if the logs are not really helpful and contain everything you need, then it's really hard to figure out where the problem is. That's one side. So logging enough is important. On the other side, if you are starting the application, you see tons of log outputs or even make it worse. If you are using a library and it prints logs and logs and logs, you cannot see your own important information and, well, it makes it hard to find the important things. So I'm also getting annoyed if I see too many logs that are not helping to me. And that corresponds to, well, we don't have that much time to look at every long, extremely long log output. It's important to have a simple way to look into it, to find the important bits and, well, use these bits and go to the problem that you want to solve. And the last point, I think, it's easy to do it. So that's the main reason I'm giving this talk. Oh, I have no idea why it's no. So about developer logs. What do I mean exactly? The developer log is something in my understanding that you see on your console when you are starting the application and there's something printed out. You can use cout or printf or anything you want to use. In this talk, it's mostly QDBug and France. When do you print something out like that? You should only do it if something happened that is important to you. It could be something that should not happen, like there's a corruption in file or there's some strange network response that you didn't expect. You can also use it at logical points in your application where you read the output and in your head you think about how is the program programmed and how should it work. And by following these points, you see where is the logic from the output not corresponding to the logic that the program has. And you can track down an error. It might also be useful to, well, to track user interactions like the user did something like downloading a file, open the website to help figuring out what the user really did with all the privacy bits applied, of course. But sometimes the user cannot remember exactly what they did and so those log information helped you to figure out what is wrong. And what also people are doing, which what I will not cover here is tracing points. So you can also use log messages to print timestamps and use it for benchmarking your program. But I think there are much better tools than log outputs, so I will not cover it. So usually what we have here is information that you print via standard out. And that's a log. There are, however, quite different levels of logs, which we all know in our lives. The most critical or most severe one is the fatal log. You might have seen it in some situations where a program says, well, that's so critical, I cannot do anything, I must shut down. For example, it might be a ralent client who is looking for a ralent server and it's not there and well, it stops working. I think maybe David will talk in the next talk about how to avoid this. But well, there are issues where you simply know this program cannot continue. And before you crash, you print out at least a meaningful log message that makes debugging a little bit easier. Then we have the critical messages, which means something cannot work. So you have to skip an operation or you are losing data. It could be that you are loading a file into your application and the file is corrupt and you cannot do anything with it. And you try not to crash, so you are aborting the whole operation, but the operation, well, it's not happening. That's a little bit different to a warning. In my understanding, a warning is something where you still know, well, that's something wrong, that's not okay. But I think or the program thinks it can continue in a meaningful way that the application still acts as expected. It could be like a duplicated file format that is not expected, but well, you can still handle it, but it should not be there in that case. But at least for the user, the program acts as the user expects it. However, you know that something is wrong in the application. So that's a warning. Then we have the info messages. And I think these are the hardest ones to differentiate from the debug messages because info means something like a business state is entered, like you open the dialogue, or the user opened the dialogue, the user downloaded the file or something that is not seldom, they're not often used and it's often triggered explicitly by user interaction or state machine state change or something like that. And differently, you have debug messages that are much more, which happen much, much more often that you often even have several times in a function that explains how the function is behaving, what are the different steps, which is the data that is currently checked. So I think in a rule of some info message, you print at most once per function and not really high frequently, in debug messages, there can be a lot of them in a single function. And the last level that I often know are trace messages. Those you usually do not use unless you are in really low level areas, working in low level areas, like in the network stacks or like in the Qt renderer. You can enable trace logging for the Qt renderer and you see really fine grained messages for every render step that is happening. And it allows you to really, really precisely debug what is going wrong. However, in the normal way, when you're using it, you never want to see these messages because you can't see anything else because there are hundreds of lines in a second that are printed out. That's not really doable to analyze, but you need a way to activate it when you really need to look into it more deeply. And well, these are the messages we want to print out in our applications, in these iterations where they are important. And that's the question, how to do it in the best way in Qt and KDE. For that, we have the Qt logging framework. I'm calling it here Qt debug, but it's a little bit more. You can essentially include the header Qt debug. Then you get most of it. The second part I will show in a few slides. If you're using it, you have different functions. You can have Qt debug, Qvarning, Qinfo, critical, Qfatal. And what is nice, they correspond to the terms I used in the previous slides because if you have a debug message or warning message or an info message or a critical message, you can use them and print them out for the respective level. So directly when writing a log message, you should think about is this a debug or is it really critical or warning because it helps you later when analyzing the program what is going wrong. The usage is quite simple. On this slide, in the following slides, I'm using only the stream API for Qt debug. There's also printfapi, which I'm not using personally, but it's documented. You can use it. I'm just a big fan of the stream API because I think it's clear about what you're doing. So how does it work? A debug message, you use Qt debug and write some text. That's super simple. And you see on the console, welcome, exclamation mark. And well, you can also print out data types. And that's really nice about Qt debug. It has printed printing for, I think, well, nearly all Qt data types, at least the commonly ones, the mostly used ones for application developers. I think it's really years ago that I had to write my own pretty printer by myself for any Q type. And this is an example where you print the current date and you get something meaningful. You get exactly this printed out Q date. And in quotation marks, the actual date in a unreadable way, which is what we expect. A different example, if you create a rectangle, then we don't get the constructor with its parameters, but we get that at position 1.2 and has size 3 times 4. And if you need more, so if you need more pretty printers, for example, also for your own data types, you can simply register it and write it and get really nice output for the most important bits for things that you want to print out if you are passing an object somewhere into the stream. Since it's a stream, you can simply construct, well, complex expressions and sentences out of it. Here, I simply used an integer, put it after academy, and then we get academy 2021. You can use several modifiers for the QD bug that you disable space, that you disable quotation marks, and then you get academy 2021 without quotation marks and without spaces. You can also put modifiers into the stream, and by that you're getting, well, I added a hex modifier and an uppercase modifier, and you get 7E5, which is 2021 and hexadecimal. And if you're ever using that, I think not that often use case, have a look at QD bug state saver, which looks similar like Qmutix locker, and saves the current state of the stream and can restore it on destruction, which is really handy if you ever get to that situation. And they are much more documented. The best way to get it, have a look in the documentation, it's really quite nice there. But that's not all. What I showed you are, well, the basic debug operations. What we actually want to have are categorized logs. The meaning is that we want to categorize logs that belong to each other in the same category. But before doing that, let's talk about what's the category and category name. In QD, Qt logging, you can create categories by simply using a string. Here it's category and add a subcategory. So you have the dot operator and then you add a subcategory. And you can add a subcategory to the subcategory and continue this as long as you want to have. I think I never used more than three levels, but everything's possible. And you can define every category you want to have. And with these categories, what I will show you on the next slide, you can enable and disable logging for specific categories or specific severities in specific categories. First, how to create or register a category. There are of course macros for that. So in your header, you have to use the QD clear logging category macro and add a name for it. And in your implementation, you use the Q logging category macro and there are two variants of it. There's the two parameter variant. And in the two parameter variant, you put the name, the same name that you used for QD clear logging category. And then you add a string. Here it would be category dot subcategory dot subcategory for example. And you have a different way where you have a different option where you have a third parameter, which is called message type. And in this option with three parameters, you can define the minimal severity for which logging output is enabled. And the important thing to know is that if you use the two parameter way, message type is set to QD bug. So if you create a logging category with this macro, every debug message is printed out. So essentially every message is printed out. If you're using this macro and set it for example to Qvroning, then you per default don't see any debug message and no info message, but warning critical messages are printed out. Fatal messages are kind of special. They don't belong to categories. But you can use that for setting default output levels. And we will see that at the next slide. Here's a really small example. So I have header loggingcategories.h. I here have to also include a QLoginCategory because I want to have these macros. So that is the way that you usually include. I call the category micro category. And in the implementation site, I'm including my header of course. And I'm using the QLoginCategory macro. Use the same name I've given the category before. And I add a name. So my category is now called myapp.foo. And I set the minimal severity level for outputs to Qvroning message. And if we are doing this and we are using the following output, here the important thing, there's a C now before there was no C. So all the QDebug, QInfo, Qvroning, QCritical functions all the ways have a C variant where you can then add the category. So we have QCDebug with the category here. And then we have a debug output in the category with the string identifier myapp.foo. And because this is debug and we set the minimal severity level to Qvroning, we will not see anything. To see anything, we have to enable this category. And that's a really nice thing because with this way, you can hide messages from users. So from library users and for end users. So you can create nearly arbitrary internal debug outputs and disable them. And simply if you need them, you can enable them. And that's really helpful in contrast to printing everything out. And I will show you how to do it. What this is called, I'm calling lock filtering that you filter your lock outputs. We are specific terms. So we have the default configuration on the previous slide. And now we can add arbitrary configurations, so arbitrary logging rules to enable or disable certain categories or logging levels in categories. From the Qt documentation, there are five ways. The first is you can put any file named Qt logging to your library info data pass. I actually never used that before. That is one of my main ways to do it. It's in my home directory, also in any file in .config Qt project. And in that file, you must add a rules section. And then you can add rules. I will show you the syntax in detail at the next slide. A different way would be inside your application to set these filter rules with the function set filter rules. And in this function, then you have to add the exact same content you would put under the rules. So really with backslash n separators for different rules. That's also something, at least to me, it's not really common. The fourth way, for me, it's mostly for embedded users, for embedded use cases, where you can pass an environment variable to your application called QtLoginConf and tell it where a file is located in which the logging rules are present. It's the same as before, but here you are using QtLoginConf variable to pointing to a specific file. And the fifth variant is what you are usually doing in your daily lives that is using QtLogin rules variable and with that you can enable logging and disable it. And that variable could look like this. This is a really simple example in line one. I set the QtLogin rules equals star equals true. And so we can also use wildcards. And with that I enable every logging for my app. So I will see everything. I will, for the previous example, also see the debug output because now I have an override and enabled everything. I can also add several rules after each other. Here I have my app.lib equals true. So for this subcategory I enable everything and for my app.backend I set everything to false and do not see anything from this category. And well, you can do every rule you want, essentially. These are the rules for creating logging rules. You always have to add a category. Optionally you can add a type after it. The type must be run of debug in for warning or critical. So it defines which severity level you are pointing to. And then you set it to true or false. For the category you can also use wildcards. But there's a restriction, which is important to know. You can put wildcards only as first or last character or both. So it is fine to use wildcard equals true. It is okay to use k dot star equals false. It is okay to use a simple rule kwrite dot debug equals true. You can use wildcard as the first. So also for every category. For every category you set critical to true. And you can also use the wildcard at beginning and at the end. So we have every subcategory with the subcategory being strange things there you will enable the log output. And the rules are read from top to bottom and applied in that sequence. And again, there's more in the Q documentation if you want to have details. And if you do not want to write these files by hand, there we have a really, really nice application in KDE that is Q debug settings. If you don't know it, please install it. I think Laurent voted some years ago. And with that application, you can simply configure Qt logging in. And even much nicer, you directly have a list of all logging categories that are available and have dropped on boxes to select the severity levels. You can even do more and edit also the environment variables. But I think the most important point is that you can simply easily activate and deactivate logging categories. And it's, I think, so simple that you can even tell a user to do that, to give you more understandable log outputs to debug a problem. There's one thing to consider to use this application and to have your logging categories listed in this list, you have to put your login categories in this location and you could do it by hand or because you're probably already using extra CMAC modules, you are just using a CMAC macro and installing them. This is just a short copy paste from the extra CMAC modules for the logging categories. I will be really fast here because there's an excellent documentation in it. You can declare your login categories in CMAC and also generate the header then. You can compile it then into the target. And the third step is you are installing the categories from the target to which you edit the logging categories and you get all the magic running that debug settings is working. And so that's really my recommendation to have a look at this. It makes your life easier. Okay, that was mostly about the developer perspective. So you are sitting in front of the application or you have the user on the other side on bugzilla sitting and want to get useful logging outputs. I also want to have a short look into the whole system. This is maybe more the look from this admin who has several client PCs where they have to only have everything working nicely. Or from my personal main use case, I'm looking on embedded devices where I have many applications running, many applications with Qt. And well, I don't want to have to look into each console output of every application to get my logs. That would be extremely tedious. And it's more problematic if I have two applications that are interacting really, really hand in hand. And I need to have both log outputs merged to each other to see how are they communicating. Examples from KDE could be a Canadian with K-Mail where you really want to see the communications on both sides. Another example could be a railing compositor and a client where you really want to have both outputs in one list and see which is coming from which and see that you have the ping-pong of messages sent from each to each other. So how to get it? Per default Qt is using the standard out log output. I think there is some kind of integration that it ends in certain system things or system log things, but you have a much better base. Qt directly natively implements several logging back ends. And for Linux that either this log or JournalD. This log is I think the traditional logging back end and JournalD is my current favorite because it's using it's extremely nice and easy to use to analyze logs there. There are also back ends for Android, for Windows, for Mac OS, but I'm not an expert in that. So I will stick here mostly to JournalD and the JournalD integration. The important point here is if you want to use that, it has to be enabled during configuration time of Qt and I'm not sure how many distributions are currently doing it. I read that Fedora enabled it. I know from my Debian system that I also see outputs in JournalD. I'm not sure how the landscape is there. I'm mostly looking at my self-compiled, self-configured Qt on my Raspberry Pi reference testing system with my self-created doctor system there. So that's on desktops a little bit different. Important point here is you must only configure one back end at once. If you're configuring two, it's undefined behavior because it's not defined which takes preference. It's a current implementation I think always JournalD wins and you only end with JournalD and you think this lock is also activated, but it's not. So you have to be careful there a little bit. If you enabled such a locking back end, then Qt bug changes the way where it prints the outputs. They are not anymore directly sent to the standard out. They are just sent to the JournalD demon. If you configured JournalD that it creates persistent locks, you have it at Valock Journal and can use it there and read it there. For accessing it, there's an extremely nicely used tool that is called a JournalCtrl. That's a CLI tool with a lot of parameters. You should really have a look and try it out. I bet it's already kind of working on your system so you can directly test it. I have missed a typo. It should be minus one. So this is an example where you just use JournalCtrl to see all locks of your previous boot. If you have systemd services for your applications, you can also filter by them. So you use the systemd unit and say I only want to have outputs for SDGM for example, for that you usually have a systemd service. You could also say I want to have it only for SDGM and only priorities 1 to 4. And I think that should map to most critical until warning. And there are a lot of more possible ways to use it. And it's extremely helpful if you analyze a system or especially also in a method system. Just one thing you should keep in mind. If you ever have problems that you don't see any lock outputs anymore on your console, set this environment variable and that enforces an additional output also in your console, also inside your IDE. If you do not see anything anymore but it lands in your logging sink, that will help you to still debug applications in your IDE. And what is really nice is to see that the Plasma team is introducing more and more systemd services, which helps making these locks nicer to analyze because they could then go up by systemd services. If you want to have colors here and not only a black and white command line interface, in my free time I created a small library. It's called a K-journody. It's currently in Playground. It's mostly an abstraction for Q-abstract item model abstraction for the Journal Control or Journal D C API. And it allows a really simple integration of lock filtering and lock analysts into any Qt-based, Qt-quick-based applications. I have a reference implementation there, Journal D browser that you can use. And I'm looking for some people who are interested to get it somewhere into Plasma, for example, just ping me. I would be gladly implemented there and get all those filtering somewhere easy to use. One last point here. My main use case here is that I often use a base that I'm using an embedded device, copies a full Journal D database onto my system and then use this tool or Journal Control to analyze it. And that's extremely powerful because you can do full offline analysis of locks, which is at least for embedded really important. Okay. And that's a screenshot from it. And I will be fast and go to the last slide. Okay. I was quite fast, I think. So the key messages that I wanted to tell you is if you do logging and you should do logging and you should do a lot of logging, all the logging that helps you to analyze it, you should do it categorized and disable it per default so that you can enable it if you need it. And that helps without recompilation to get, well, helpful outputs and helpful bug reports from users. And what I really recommend is try these tools for analyzing Journal Control, Journal D with Journal Control and K Journal D browser and see what is already possible right now. Okay. Thanks for keeping with me. I think I'm at the end of my time, at least at the end of my slides. So thank you. And the rest, yes, you are perfectly on time. We have just a few minutes for some questions. I see two of them. So both from Kai. The first one, I think you might have answered, but anyway. So in the hierarchy, is there a difference in enabling foo equal true and foo dot star equal true? Yes. Because the write card is only allowed for categories. So with foo equals true, you enable everything a category foo. With foo dot star equals true, you enable everything in this, in any subcategory of foo. So foo directly is, should not be enabled, I think. I would have to check, but I'm quite confident that this will not work. Thanks. So the second question is, can we have a magic macro to annotate, to annotate the login rules and extract them from the source code without having to create a dedicated login categories file? Interesting. I'm just thinking, isn't that all, that what XS CMake modules isn't already doing? Because we are, well, we have three macros, that's maybe a little bit more, too much. With that, we are generating the header, we are generating the implementation, and we are installing them. Maybe for convenience, it could be aggregated into one simple macro. I think that could be a nice way. Good point. I think I will look into it. Thanks. Thanks, Guy, for that. I don't see other questions, and I think people should have figured out by now how to add questions. We are always, remember, now we have even a properly named widgets in the matrix rooms. So thanks for the people who fixed that. So you have now the Q&A widget. It should be easier to find that. So I guess we are at the end. So thanks again, Andreas, for the talk, and yeah, we will continue in four minutes with the next talk.