Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NLog's schema (NLog.xsd) ignored in Visual Studio? #256

Open
fterrani opened this issue Oct 2, 2024 · 11 comments
Open

NLog's schema (NLog.xsd) ignored in Visual Studio? #256

fterrani opened this issue Oct 2, 2024 · 11 comments

Comments

@fterrani
Copy link

fterrani commented Oct 2, 2024

Hello.

I was looking for documentation on the <nlog> element in NLog config files. The best I've found so far is the XML schema.

While examining it, I noticed this line:

<xs:element name="quoteChar" type="xs:string" minOccurs="0" maxOccurs="1" default=""" />

I think the """ might make some XML Schema parsing tools fail. It should probably be:

<xs:element name="quoteChar" type="xs:string" minOccurs="0" maxOccurs="1" default="&quot;" />
@fterrani
Copy link
Author

fterrani commented Oct 2, 2024

After testing in Visual Studio and comparing behavior when editing a Maven POM file (written in XML, uses a publicly downloadable XML Schema just like NLog), the POM file's <xs:documentation> content shows up properly in tooltips while NLog's doesn't.

That might be caused by that unescaped double quote.

For those interested, note that you must first enable automatic XML schema download in Visual Studio in Tools > Options... > Text Editor > XML > Miscellaneous > Automatically downlaod DTDs and schemas.

@snakefoot
Copy link
Contributor

snakefoot commented Oct 2, 2024

Not sure where the problem is since this is the value in the online XSD-file:
https://github.com/NLog/NLog.github.io/blob/96f92cd7086a014f3d04b02d9b9e32c5814f24a0/schemas/NLog.xsd#L2625C11-L2625C104
And this is the value in the latest NLog.Schema-nuget-package:

          <xs:element name="quoteChar" type="xs:string" minOccurs="0" maxOccurs="1" default="&quot;" />

@fterrani
Copy link
Author

fterrani commented Oct 3, 2024

TL;DR: To me, the root <nlog> element shown in configuration examples uses an ambiguous way to refer to the XML Schema. I think the xsi:schemaLocation attribute should be used to both make things clear and make it work in Visual Studio.

Detailed reasoning (that's long, sorry...):

@snakefoot I discovered several interesting things that explain what you and I observed.

I thought the schema was badly written at first but it seems browsers interpret HTML character references when displaying the document's source code... &quot; are displayed as " when viewing source but the actual content once saved is indeed &quot;... Well. Sorry about this!

This leaves us with Visual Studio failing to retrieve the schema... and here things become very interesting.

Visual Studio fails to retrieve NLog's schema because NLog seem to rely on the fact that the document processor will try to fetch the XML schema using the XML namespace as a URI. Which is apparently NOT guaranteed to work according to https://www.w3.org/TR/xmlschema11-1/#schema-loc :

The author of a document uses namespace declarations to indicate the intended interpretation of names appearing therein; it is possible but not guaranteed that a schema is retrievable via the namespace name. Accordingly whether a processor's default behavior is or is not to attempt such dereferencing, it must always provide for user-directed overriding of that default.

[...]

On the other hand, in case a document author (human or not) created a document with a particular schema in view, and warrants that some or all of the document conforms to that schema, the xsi:schemaLocation and xsi:noNamespaceSchemaLocation [attributes] are provided.

Since namespace specified in xmlns attribute is not guaranteed to contain an XML Schema URI, Visual Studio chooses not to use it as such (on top of that, auto download is even disabled by default).

According to W3C, xsi:schemaLocation can be used to fix this ambiguous situation:

<root xmlns="$namespace"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="$namespace $uri_to_schema">
    <!-- document content -->
</root>

Where $namespace is strictly speaking an "IRI" (basically a URI where Unicode characters are used directly?), and $uri_to_schema is... well, you get it.

Current NLog example files don't use xsi:schemaLocation and declare an xsi namespace... that they don't use at all (!) which makes the intention unclear to me:

<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <!-- document content -->
</nlog>

Here is how Maven does it in its POM XML files:

<project
  xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <!-- document content -->
</project>

Which finally leads us to my final suggestion to use the following in NLog config files:

<nlog xmlns="http://www.nlog-project.org/NLog-config"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.nlog-project.org/NLog-config https://nlog-project.org/schemas/NLog.xsd">
    <!-- document content -->
</nlog>

I arbitrarily chose the http://www.nlog-project.org/NLog-config namespace IRI but it could be anything and if used as a URI, return anything. For example:

I would recommend doing what W3C does (a short HTML page explaining what a NLog.config file is and containing a link to the XSD file maybe).

@fterrani
Copy link
Author

fterrani commented Oct 3, 2024

My bad, xsi is used in the <target> elements.

Also, I tried playing a little in Visual Studio, changing the URI and attributes (xmlns, xmlns:xsi, xsi:schemaLocation).

Annotations are displayed either if (case 1):

  • the xmlns attribute is the only one present and contains a URI pointing to an XML schema
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd">
	<!-- document content -->
</nlog>

OR if (case 2):

  • the xmlns attribute is present and contains a URI pointing to an XML schema
  • AND the xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" is present
  • AND the xsi:schemaLocation attribute is present and contains a (namespace;schema URI) pair where the schema URI points to an XML Schema.
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	  xsi:schemaLocation="whatever https://nlog-project.org/schemas/NLog.xsd">
	<!-- document content -->
</nlog>

With case 2, xsi:schemaLocation's namespace is... ignored and the schema URI overrides xmlns's.

Also note that www.nlog-project.org doesn't respond when used with HTTPS so whatever we choose, we should avoid using a URI starting with https://www.nlog-project.org/.

@snakefoot
Copy link
Contributor

snakefoot commented Oct 5, 2024

Strange both FireFox and Egde can load this https https://nlog-project.org/schemas/NLog.xsd

When not using https then redirected to https://nlog-project.org/schemas/NLog.xsd?r=redirect

Maybe https only works when not including www in the URL ?

Really appreciate these investigations, as I would like to have the NLog Wiki / Examples to work out of the box.

@fterrani
Copy link
Author

fterrani commented Oct 5, 2024

No problem, I'm interested in understanding that issue :)

I need to read more specifications and articles and to do some testing on my side. My goal is to have the XML schema annotations to show up in Visual Studio and as many editors as possible, while coming up with a sensible and standard-conforming way of doing it.

Note that to me, standards matter and I do not intend to cater to each and every Visual Studio quirk :) For example, the fact that it seems to fetch the schema if only xmlns is used and contains a valid URL is not a reason to do it to me!

I'm also open to contributing by updating the schema if necessary. For example, the following line didn't validate if I remember correctly (while coming directly from https://github.com/NLog/NLog/wiki/Getting-started-with-ASP.NET-Core-6)

<logger name="Microsoft.Hosting.Lifetime" minlevel="Info" writeTo="lifetimeConsole, ownFile-web" final="true" />

It's due to the (|([a-zA-Z][a-zA-Z0-9_\-]*))(,([a-zA-Z][a-zA-Z0-9_\-]*))* regex not accepting spaces in the NLogTargetIDList simple type in the schema. Note that it also accepts that kind of values:

  • ,a
  • ,a,b
  • ,a,b,c

@snakefoot
Copy link
Contributor

snakefoot commented Oct 9, 2024

Think it should be fixed so spaces are allowed in writeTo. Since the NLog Config Parser supports it just fine, and feels more readable.

@fterrani fterrani changed the title Unescaped double quote in NLog's schema (NLog.xsd) on the quoteChar attribute? NLog's schema (NLog.xsd) ignored in Visual Studio? Oct 22, 2024
@fterrani
Copy link
Author

Hi again.

I'm finally back regarding this issue. It took me a long time since I had to research this in depth, read specs, articles etc. I'll try to keep this as concise as possible (which is a challenge). Some of the things I'm gonna say might not be 100% accurate, but I think they are enough in the context of this conversation. For the sake of understanding, I have to give some info about the basics. Sorry if you already know some of this.

First, an XML namespace's purpose is to uniquely define elements and attributes, which are used in some kind of context. E.g. is <lead> a metal? or a verb about managing people? Also:

  • An XML namespace has a unique name which is an IRI (this means it must conform to the URI syntax and avoid any kind of % escaping and favor lowercase characters especially in scheme and host parts).
    • It does not have to be an HTTP(S) URI (see previous URI syntax link).
    • It does not have to return anything if used in a browser.
    • An XML namespace name is a string identifier before anything else which can be compared to another namespace name to decide if they are the same.
    • Forcing it to be an IRI allows that comparison to be as simple as possible (exactly the same characters => same namespace).
    • It should be unique of course. Which is why HTTP(S) URI are often used since they will contain a domain name which is administered globally and guaranteed to be unique.
  • An XML schema describes the content of an XML namespace. The schema is associated to the XML namespace using the targetNamespace attribute.
  • An XML document can contain a hint to retrieve the XML schema in the schemaLocation attribute.
    • schemaLocation has some weird formatting. It must contain space-separated XML namespace name and schema location couples, e.g. ns1 location1 ns2 location2 ns3 location3
  • Finally, an XML namespace can be referred to using a prefix (e.g. xs:) in an XML document.
    • The prefix cannot be xml: but besides that, nothing prevents you from using the wowthatssocool: prefix instead of xs: when describing an XML schema. You can even have no prefix (e.g. write <element> instead of <xs:element> in your schema).
    • A prefix must be bound to an XML namespace using an xmlns:myprefix="..." attribute.
  • It is possible not to use any prefix. In that case, simply do xmlns="..." and any unprefixed element will belong to the namespace referred to by xmlns.

IMPORTANT: There is a fundamental difference between an XML namespace's name and the location of that XML namespace's schema! The name is a unique identifier, nothing else. The schema's location is a place where a schema describing that namespace can be found. Unfortunately, they both tend to start with http(s): and very often the namespace name can be used in a browser to retrieve something. Needless to say this brings MAJOR confusion...

The "Visual Studio-compatible", fastest, simplest and most backward-compatible thing to do, is to add a schemaLocation and use the current schema HTTP URI as both the XML namespace name and the schema location URL used to retrieve it. It probably won't feel very satisfying to purists, but this is 100% allowed by W3C standards:

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd http://www.nlog-project.org/schemas/NLog.xsd">
      <!-- XML document content -->
</nlog>

But this is the super-careful approach. I would personally prefer something cleaner, but it's up to NLog's dev team (if this is used, the targetNamespace attribute in the schema must contain the value of xmlns):

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="https://nlog-project.org/xml-namespaces/NLog-config" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="https://nlog-project.org/xml-namespaces/NLog-config https://nlog-project.org/schemas/NLog.xsd">
      <!-- XML document content -->
</nlog>

Note that most of the time, using the namespace name in a browser allows to retrieve something. The most frequent thing I could see is that using the XML namespace name in a browser leads to a page explaining shortly what the XML namespace represents in a human-readable HTML document (status code 200, a normal page really). Some return 404 which is totally allowed (XML namespace names are neither required nor forbidden to lead anywhere at all).

Here are the details about what happens when using the XML namespace name in a browser:

  • Visual Studio csproj, Maven, XAML and BPMN XML namespace names all return a 404 page (which is totally OK)
  • XUL (Mozilla), MathML, Open Document Format (odt, ods...) and almost all W3C XML-related or SVG namespace names return an HTML document explaining the namespace (content, link to specification etc.).
  • SOAP namespace name returns the XML schema directly (and seem to use an undeclared xs: prefix without declaring any namespace for it which goes against the XML Namespaces specification).
  • XMPP ony uses namespace names with the urn: and jabber: URI schemes
  • Open Document Format also sometimes uses urn: schemes in namespace names
  • XMI (OMG) namespace name displays a list of folders
  • ...

Also, it is required to define prefixes in XML documents ; xs: must be bound to a namespace. So the following attribute and value must be used in the NLog schema: xmlns:xs="http://www.w3.org/2001/XMLSchema". Which gives this:

<?xml version="1.0" encoding="utf-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" id="NLog" targetNamespace="http://www.nlog-project.org/schemas/NLog.xsd" elementFormDefault="qualified">
    <!-- NLog.xsd schema content -->
</xs:schema>

About Visual Studio's behavior, I think the spec considers it an XML processor and if so, they can basically do anything they want regarding schema retrieval. The XML schema spec simply defines specific terms and their meaning to provide some standard vocabulary, but does not enforce anything. See notably section C.2:

Conforming processors may implement any combination of the following strategies for locating schema components, in any order. They may also implement other strategies.

That being said, I still find Visual Studio's behavior strange. Note however that it can be due to the current root tag attributes used in NLog.xsd and NLog XML config documents / snippets. This remains to be tested. My guess is that Visual Studio should better behave.

@snakefoot
Copy link
Contributor

snakefoot commented Oct 23, 2024

Thank you for this detailed investigation. If the following example provides working intellisense, then I will probably go with that:

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd http://www.nlog-project.org/schemas/NLog.xsd">
      <!-- XML document content -->
</nlog>

But I will try and play with this myself in the weekend, if I can find the time. But agree that all wiki-examples should be updated, so intellisense will be working out of the box.

Guess there is bonus task for NLog v6 to produce a NLog.xsd schema-package, that includes intellisense for NLog Targets/Layouts that comes from the soon many official extension-nuget-packages.

@fterrani
Copy link
Author

You're welcome :)

During your tests, don't forget that NLog.xsd must also be fixed since it currently does not bind any namespace to the xs: prefix (see above).

I don't know how what I've written will translate to concrete development tasks, but I remain available here if you need me.

@snakefoot
Copy link
Contributor

snakefoot commented Nov 4, 2024

Notice I have not forgotten you, and my promise to test myself and make a decision. But my limited spare time is right now spent on re-writing NLog for ver. 6.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants