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

Unity Support in v2 #132

Merged
merged 8 commits into from
Dec 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions .github/workflows/build-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,15 @@ env:
GIT_TAG: ${{ github.event.inputs.tag }}

jobs:
dotnet-build-and-push:
update-packagejson:
uses: Cysharp/Actions/.github/workflows/update-packagejson.yaml@main
with:
file-path: ./src/ZLogger.Unity/Assets/ZLogger.Unity/package.json
tag: ${{ github.event.inputs.tag }}
dry-run: ${{ fromJson(github.event.inputs.dry-run) }}

build-dotnet:
needs: [update-packagejson]
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
Expand Down Expand Up @@ -42,4 +50,11 @@ jobs:
tag_name: ${{ env.GIT_TAG }}
release_name: Ver.${{ env.GIT_TAG }}
draft: true
prerelease: false
prerelease: false

cleanup:
if: needs.update-packagejson.outputs.is-branch-created == 'true'
needs: [update-packagejson, build-dotnet]
uses: Cysharp/Actions/.github/workflows/clean-packagejson-branch.yaml@main
with:
branch: ${{ needs.update-packagejson.outputs.branch-name }}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,5 @@ src/ZLogger.Unity/ZLogger.Unity.sln
src/ZLogger.Unity/ZString.csproj

src/ZLogger.Unity/*.csproj

.DS_Store
92 changes: 90 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ ZLogger
===
[![GitHub Actions](https://github.com/Cysharp/ZLogger/workflows/Build-Debug/badge.svg)](https://github.com/Cysharp/ZLogger/actions) [![Releases](https://img.shields.io/github/release/Cysharp/ZLogger.svg)](https://github.com/Cysharp/ZLogger/releases)

**Z**ero Allocation Text/Structured **Logger** for .NET with StringInterpolation and Source Generator, built on top of a `Microsoft.Extensions.Logging`.
**Z**ero Allocation Text/Structured **Logger** for .NET and Unity, with StringInterpolation and Source Generator, built on top of a `Microsoft.Extensions.Logging`.

The usual destinations for log output are `Console(Stream)`, `File(Stream)`, `Network(Stream)`, all in UTF8 format. However, since typical logging architectures are based on Strings (UTF16), this requires additional encoding costs. In ZLogger, we utilize the [String Interpolation Improvement of C# 10](https://devblogs.microsoft.com/dotnet/string-interpolation-in-c-10-and-net-6/) and by leveraging .NET 8's [IUtf8SpanFormattable](https://learn.microsoft.com/en-us/dotnet/api/system.iutf8spanformattable?view=net-8.0), we have managed to avoid the boxing of values and maintain high performance by consistently outputting directly in UTF8 from input to output.

Expand Down Expand Up @@ -63,6 +63,9 @@ This library is distributed via NuGet, supporting `.NET Standard 2.0`, `.NET Sta

In the simplest case, you generate a logger by adding ZLogger's Provider to Microsoft.Extensions.Logging's [LoggerFactory](https://learn.microsoft.com/en-us/dotnet/core/extensions/logging), and then use ZLogger's own ZLog method.

For Unity, the requirements and installation process are completely different. See the [Unity](#unity) section for details.


```csharp
using Microsoft.Extensions.Logging;
using ZLogger;
Expand Down Expand Up @@ -716,7 +719,92 @@ public class Foo

Unity
---
This library requires C# 10.0, however currently Unity C# version is 9.0. Therefore, it is not supported at this time and will be considered when the version of Unity's C# is updated.

### Installation

ZLogger uses some of the compile time features of C# 10, and ZLogger.Generator uses some of the features of C# 11.

To use them in Unity, needs to check the Unity version and set up the compiler.

- Unity 2022.2 or newer
- Standard ZLogger features are available.
- Unity internally embeds the .NET SDK 6. So C# 10 is available via compiler arguments.
- Unity 2022.3.12f1 or newer
- ZLogger source generator available.
- Unity internaly update .NET SDK 6. So C# 11 features are in preview.

Prerequirements:
- Install [NuGetForUnity](https://github.com/GlitchEnzo/NuGetForUnity)
- Required to install the dlls of ZLogger and its dependencies.
- Install [CsprojModifier](https://github.com/Cysharp/CsprojModifier)
- Required too develop in the IDE with a new language version.

Installation steps:

1. Setup the C# compiler for unity.
- Add a text file named `csc.rsp` with the following contents under your Assets/.
- ```
-langVersion:preview -nullable
```
- Note:
- If you are using assembly definition, put it in the same folder as the asmdef that references ZLogger.
- Specifying `langVersion:10` allows C# 10 features to be used. `langVersion:preview` allows parts of C# 11 features to be used.

2. Setup the C# compiler for your IDE.
- Add a text file named LangVersion.props with the following contents
- ```xml
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<LangVersion>preview</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
```
- Open Project Settings and [C# Project Modifier] section under the [Editor].
- Add the .props file you just created, to the list of [Additional project imports].
- Note:
- If you are using assembly definition, add your additional csproj in the list of [The project to be addef for import].
3. Install ZLogger nuget package.
- Open [Nuget] -> [Manage Nuget Packages] in the menu bar.
- Search `ZLogger`, and press [Install].
4. If you want to use the ZLogger extension for Unity, install the `ZLogger.Unity` package.
- Add `https://github.com/Cysharp/ZLogger.git?path=src/ZLogger.Unity/Assets/ZLogger.Unity` to Package Manager


### Basic usage

The basic functions of ZLogger are also available in Unity as follows. Use LoggerFactory directly to create loggers.

```cs
var loggerFactory = LoggerFactory.Create(logging =>
{
logging.AddZLoggerFile("/path/to/logfile", options =>
{
options.UseJsonFormatter();
options.IncludeScopes = true;
});
});

var logger = loggerFactory.CreateLogger(nameof(YourClass));

var name = "foo";
logger.ZLogInformation($"Hello, {name}!");
```

### UnityEngine.Debug log provider

If you have installed ZLogger.Unity, you can output to UnityEngine.Debug.Log as follows

```cs
var loggerFactory = LoggerFactory.Create(logging =>
{
logging.AddZLoggerUnityDebug(options =>
{
options.UseJsonFormatter();
});
});
```


License
---
Expand Down
127 changes: 127 additions & 0 deletions src/ZLogger.Generator/Shims/CSharpSyntaxHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

// Copied from https://github.com/dotnet/runtime/blob/1473deaa50785b956edd7d078e68c0581c1b4d95/src/libraries/Common/src/Roslyn/CSharpSyntaxHelper.cs

using System.Collections.Generic;
using System.Diagnostics;

using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace Microsoft.CodeAnalysis.DotnetRuntime.Extensions
{
internal sealed class CSharpSyntaxHelper : AbstractSyntaxHelper
{
public static readonly ISyntaxHelper Instance = new CSharpSyntaxHelper();

private CSharpSyntaxHelper()
{
}

public override bool IsCaseSensitive
=> true;

public override bool IsValidIdentifier(string name)
=> SyntaxFacts.IsValidIdentifier(name);

public override bool IsAnyNamespaceBlock(SyntaxNode node)
=> node is BaseNamespaceDeclarationSyntax;

public override bool IsAttribute(SyntaxNode node)
=> node is AttributeSyntax;

public override SyntaxNode GetNameOfAttribute(SyntaxNode node)
=> ((AttributeSyntax)node).Name;

public override bool IsAttributeList(SyntaxNode node)
=> node is AttributeListSyntax;

public override void AddAttributeTargets(SyntaxNode node, ref ValueListBuilder<SyntaxNode> targets)
{
var attributeList = (AttributeListSyntax)node;
var container = attributeList.Parent;
Debug.Assert(container != null);

// For fields/events, the attribute applies to all the variables declared.
if (container is FieldDeclarationSyntax field)
{
foreach (var variable in field.Declaration.Variables)
targets.Append(variable);
}
else if (container is EventFieldDeclarationSyntax ev)
{
foreach (var variable in ev.Declaration.Variables)
targets.Append(variable);
}
else
{
targets.Append(container);
}
}

public override SeparatedSyntaxList<SyntaxNode> GetAttributesOfAttributeList(SyntaxNode node)
=> ((AttributeListSyntax)node).Attributes;

public override bool IsLambdaExpression(SyntaxNode node)
=> node is LambdaExpressionSyntax;

public override SyntaxToken GetUnqualifiedIdentifierOfName(SyntaxNode node)
=> ((NameSyntax)node).GetUnqualifiedName().Identifier;

public override void AddAliases(SyntaxNode node, ref ValueListBuilder<(string aliasName, string symbolName)> aliases, bool global)
{
if (node is CompilationUnitSyntax compilationUnit)
{
AddAliases(compilationUnit.Usings, ref aliases, global);
}
else if (node is BaseNamespaceDeclarationSyntax namespaceDeclaration)
{
AddAliases(namespaceDeclaration.Usings, ref aliases, global);
}
else
{
Debug.Fail("This should not be reachable. Caller already checked we had a compilation unit or namespace.");
}
}

private static void AddAliases(SyntaxList<UsingDirectiveSyntax> usings, ref ValueListBuilder<(string aliasName, string symbolName)> aliases, bool global)
{
foreach (var usingDirective in usings)
{
if (usingDirective.Alias is null)
continue;

if (global != usingDirective.GlobalKeyword.Kind() is SyntaxKind.GlobalKeyword)
continue;

var aliasName = usingDirective.Alias.Name.Identifier.ValueText;
var symbolName = usingDirective.Name.GetUnqualifiedName().Identifier.ValueText;
aliases.Append((aliasName, symbolName));
}
}

public override void AddAliases(CompilationOptions compilation, ref ValueListBuilder<(string aliasName, string symbolName)> aliases)
{
// C# doesn't have global aliases at the compilation level.
return;
}

public override bool ContainsGlobalAliases(SyntaxNode root)
{
// Global usings can only exist at the compilation-unit level, so no need to dive any deeper than that.
var compilationUnit = (CompilationUnitSyntax)root;

foreach (var directive in compilationUnit.Usings)
{
if (directive.GlobalKeyword.IsKind(SyntaxKind.GlobalKeyword) &&
directive.Alias != null)
{
return true;
}
}

return false;
}
}
}
Loading
Loading