Comments (7)
I really like the idea! I think it could be very useful to first hide some details!
I'm curious about the following things:
- what about memory usage if we keep storing messages in the dictionary? Should we restart things if it get too much?
- can we configure everything from the (xml) config?
- is a message box the most ideal for displaying the details?
- it was not fully clear to me, does this only works with exceptions?
Further I would recommend to send as much possible info to the link clicked handler (at least the full logeventinfo)
Maybe it's also an idea to let the details be layoutable? So it can be full set from the config and you have full control to show.
from nlog.windows.forms.
I'm very pleased that you liked my idea! Hope we could get it to release state!
it was not fully clear to me, does this only works with exceptions?
The original solution — yes, because it was what I designed it for. But I really feel this should be generalized. But I'm not sure — how exactly?
Maybe it's also an idea to let the details be layoutable?
Yes, I also thought so, but what would it be? A separate specific layout renderer? If yes, then would it be possible to show the link only for some specific messages? Probably with onexception
layout renderer? But are there other options, if we decide it to be more generalized? Some other conditional layout rendereres based on logEvent params? Showing links for every line would be an overkill I think.
Another problem is how to attach logEventInfo to the link? For now I'm just parsing the clicked link text, getting integer exception id which I have to place there. It looks like "details N1", "details N2", which is not too great. It should be possible to have hidden urls for the links, but I was not able to make it work yet. Any help on this would be appreciated.
So I assume that it's possible to attach some kind of marker to the link, which leads us to following question:
what about memory usage if we keep storing messages in the dictionary? Should we restart things if it get too much?
Yes, for now I don't clear the dictionary, and it should be changed. I suppose it should be possible to detect what markers are being deleted when removing first lines on maxLines
overflow, and then delete corresponding items from dictionary. Still this would require parsing RTF. Do you see a better way?
is a message box the most ideal for displaying the details?
Surely no, I'm showing this as a simplest example. For our purposes I designed a special form pretty-printing stacks and showing Exception.Data contents. Still I did not mean to bind any specific behaviour to link click event, therefore I'm exposing RichTextBoxExceptionTarget.ExceptionLinkClicked
event (should be renamed if we generalize things), and allowing to get a target bound to specific control via static RichTextBoxExceptionTarget.GetTargetForControl(control)
call. See my code above. I think it's not that bad design-vise.
can we configure everything from the (xml) config?
Besides my thoughts on layouts above, do you mean here that we could also bind a specific OnLinkClicked behaviour in the (xml) config as well? I'm not sure on this, do you have any proposals on how it could work?
from nlog.windows.forms.
I also remembered another use-case for this feature. In one of our applications we had to switch to manual writing texts to RTB control instead of logging to be able to add links for some messages.
The application itself is about doing some transformations with an Excel file. We display some part of the file in a tabular listview. So when a processing routine finds some problematic cells it prints a message to a RTB including a link, click on which causes preview to focus on a problematic cell.
So this could work as a real-world use-case for using links without exceptions. I would really like to use NLog for logging there without missing link functionality.
from nlog.windows.forms.
Ok, here's what I have been thinking on generalization and configuration of links. We surely don't want to add links to all messages being logged, we want to have ability to specify which messages have links and how links look like.
So I think we can achieve these goals by creating specific layout renderer, let's say ${rtb-link: link-text=Layout}
. So only events that had rtb-link renderer active would have the links. The link text would be configurable by link-text
parameter of the layout. And we would be able to use ${onexception}
and ${when}
conditional wrappers we would be able to show the links only when needed.
Now the problem is in how the renderer would actually work. First of all it would need to create some kind of token so the logEventInfo could be found when the link is clicked.
The only solution I was able to come with, is to have a global counter and store it in the LogEventInfo.Properties during renderer.Append call. Something like this:
[LayoutRenderer("hello-world")]
public class RtbLinkLayoutRenderer : LayoutRenderer
{
internal const string PropertyName = "RTB-link-id";
private static int s_id = 0;
public Layout LinkText {get;set}
protected override void Append(StringBuilder builder, LogEventInfo logEvent)
{
builder.Append(LinkText.Render(logEvent));
int id = Interlocked.Increment(ref s_id);
logEvent.Properties[PropertyName] = id;
}
}
public sealed class RichTextBoxTarget
{
private readonly ConcurrentDictionary<object, LogEventInfo > linkedEvents;
protected override void Write(LogEventInfo logEvent)
{
...
object linkId;
if (logEvent.Properties.TryGetValue(RtbLinkLayoutRenderer.PropertyName, out linkId))
{
linkedEvents[linkId] = logEvent;
}
}
}
So this looks a bit ugly (we are adding our custom property to user-space Properties) but it should work. What worries me is how to make the needed part of the text in the RTB control to be a link. The code I use now looks like this:
public static void InsertLink(this RichTextBox textBox, string text, string hyperlink, int position)
{
textBox.SelectionStart = position;
//Does not actually work ((
//See: http://stackoverflow.com/questions/2850575/what-is-the-rtf-syntax-for-a-hyperlink
//textBox.SelectedRtf = @"{\rtf1{\field{\*\fldinst{HYPERLINK ""mysuperlink_val""}}{\fldrslt{mysuperlink}}}}";
textBox.SelectedRtf = @"{\rtf1\ansi " + text + @"\v #" + hyperlink + @"\v0}";
textBox.Select(position, text.Length + hyperlink.Length + 1);
textBox.SetSelectionLink(true);
textBox.Select(position + text.Length + hyperlink.Length + 1, 0);
}
private static void SetSelectionStyle(this RichTextBox textBox, UInt32 mask, UInt32 effect)
{
CHARFORMAT2_STRUCT cf = new CHARFORMAT2_STRUCT();
cf.cbSize = (UInt32)Marshal.SizeOf(cf);
cf.dwMask = mask;
cf.dwEffects = effect;
IntPtr wpar = new IntPtr(SCF_SELECTION);
IntPtr lpar = Marshal.AllocCoTaskMem(Marshal.SizeOf(cf));
Marshal.StructureToPtr(cf, lpar, false);
IntPtr res = SendMessage(textBox.Handle, EM_SETCHARFORMAT, wpar, lpar);
Marshal.FreeCoTaskMem(lpar);
}
So it seems that I cant just put some markup into the message text via layout renderer. I would need to call some methods on the RTB control to make the link magic work. And I don't see a way to do it with the layout renderer approach. Except to pass a lot of data via logEventInfo.Properties (actually a single object with all the needed data) and do the necessary processing in RichTextBoxTarget.Write
. But this looks like an overkill.
What do you think?
from nlog.windows.forms.
Ok, here's what I have been thinking on generalization and configuration of links. We surely don't want to add links to all messages being logged, we want to have ability to specify which messages have links and how links look like.
So I think we can achieve these goals by creating specific layout renderer, let's say ${rtb-link: link-text=Layout}. So only events that had rtb-link renderer active would have the links. The link text would be configurable by link-text parameter of the layout. And we would be able to use ${onexception} and ${when} conditional wrappers we would be able to show the links only when needed.
Sounds great!
Now the problem is in how the renderer would actually work. First of all it would need to create some kind of token so the logEventInfo could be found when the link is clicked.
The only solution I was able to come with, is to have a global counter and store it in the LogEventInfo.Properties during renderer.Append cal
You can also use LogEventInfo.SequenceID
, or a new guid.
So it seems that I cant just put some markup into the message text via layout renderer.
I don't see that. You can generate a link text with a string
? And we can render the layout
to a string
? We can render it before it has been clicked?
Or is it the problem that we need the whole LogEventInfo
? Maybe we could store it at the GDC/MDC or something like that?
from nlog.windows.forms.
You can also use LogEventInfo.SequenceID, or a new guid.
Thanks for a SequenceID idea, I've used it in a latest rewrite! Also used GUIDs in another part.
from nlog.windows.forms.
closed by #14
from nlog.windows.forms.
Related Issues (20)
- net6.0-windows
- Support Layoutable properties HOT 3
- Fix default layout comments
- Breaking changes version 5
- Change AssemblyVersion to 5.0.0.0
- Using Only NLog.config for RichTextBox Does Not Log HOT 3
- Add net6.0-windows since LTS
- Continuous Build not running on merge to master HOT 1
- NullReferenceException in RichTextBoxTarget SendTheMessageToRichTextBox
- NLog.LogManager.Configuration reload causes RichTextBoxTarget to create a new form HOT 6
- Question: Is it possible to write to winforms ListView? HOT 2
- Form hanging when RichTextBox target nonstop logging HOT 6
- Symbols package publishing failed. HOT 1
- System.NullReferenceException calling ReInitializeAllTextboxes() HOT 5
- Missing source package on NuGet HOT 4
- RichTextBoxTarget - An exception occurred under NET48 HOT 3
- Create a nuget package with net5.0-windows platform supporting HOT 3
- No RowColor for RichTextBoxTarget when LeftToRight.Yes set HOT 3
- When to support net6.0 platform? HOT 2
- PublishSingleFile causes Target symbol-name is unknown: 'RichTextBox' HOT 16
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from nlog.windows.forms.