Skip to content

Instantly share code, notes, and snippets.

@shanecelis
Last active October 21, 2021 08:59
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shanecelis/549ee612b67b13620cd0f9c95e34c853 to your computer and use it in GitHub Desktop.
Save shanecelis/549ee612b67b13620cd0f9c95e34c853 to your computer and use it in GitHub Desktop.
DebugOnce.Log() logs only once from each source code location.
/* Original code[1] Copyright (c) 2021 Shane Celis[2]
Licensed under the MIT License[3]
This comment generated by code-cite[4].
[1]: https://gist.github.com/shanecelis/549ee612b67b13620cd0f9c95e34c853
[2]: https://twitter.com/shanecelis
[3]: https://opensource.org/licenses/MIT
[4]: https://github.com/shanecelis/code-cite
*/
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine;
/** Have a chatty warning in Unity that you want to be aware exists but not in
every instance? Use DebugOnce to ensure that it will log once from that
location in your code, but not more than once.
For example, suppose you have a script that calls `LogWarning` once.
```
Debug.LogWarning("No collider given; disabling.")
```
But it has multiple instances, so one line of code emits all these entries:
```
No collider given; disabling.
No collider given; disabling.
No collider given; disabling.
No collider given; disabling.
No collider given; disabling.
```
This class allows one to only emit the warning once per call location from
the file by changing "Debug" to "DebugOnce".
```
DebugOnce.LogWarning("No collider given; disabling.")
```
On scene changes, it may be advisable to run `DebugOnce.Clear()` so that
warnings will be emitted once again.
* Not all Debug overloads are implemented. For formatting overloads, you can
add them yourself but I would suggest switching from `("A {} is acting
up.", component.name)` to using C#'s string interpolation `($"A
{component.name} is acting up.")`
*/
public static class DebugOnce {
// XXX: May want to use a bloom filter if you're very concerned about hash
// collisions.
private static HashSet<int> seen;
public static void Log(string message,
Object context = null,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0) {
if (Allow("Log", memberName, sourceLineNumber, sourceLineNumber))
Debug.Log(message, context);
}
public static void LogWarning(string message,
Object context = null,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0) {
if (Allow("LogWarning", memberName, sourceLineNumber, sourceLineNumber))
Debug.LogWarning(message, context);
}
public static void LogError(string message,
Object context = null,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0) {
if (Allow("LogError", memberName, sourceLineNumber, sourceLineNumber))
Debug.LogError(message, context);
}
/** Allow will return true once for any given sequence of identifiers. */
public static bool Allow(params object[] identifiers) {
if (seen == null)
seen = new HashSet<int>();
int hash = 0;
foreach (var id in identifiers)
hash ^= id.GetHashCode();
return seen.Add(hash);
}
/** Reset. */
public static void Clear() {
seen.Clear();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment