Skip to content

Commit

Permalink
Merge pull request #132 from Cysharp/hadashiA/unity-support2
Browse files Browse the repository at this point in the history
Unity Support in v2
  • Loading branch information
neuecc committed Dec 15, 2023
2 parents 6bf4ba6 + d1abb18 commit 39d1a4c
Show file tree
Hide file tree
Showing 82 changed files with 6,670 additions and 7 deletions.
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

0 comments on commit 39d1a4c

Please sign in to comment.