Add logline that indicates from which method the logging method is called - c#

I am adding logs to my projects. The logline shows timestamp + current method + current program. I do know how to check currentmethod, but this will always be the loggingmethod itself. How do I find the method that called for the logging method?
The coded attached is doing exactly what I want.
But it would be nice to add the part that gives the current method and project (this.GetType().Name+ currentMethodName) in the actual LogMessageToFile method.
LOGGER.cs
using System.IO;
using System;
namespace LoggerSpace
{
class Logger {
public string GetTempPath()
{
string path = System.Environment.GetEnvironmentVariable("TEMP");
if (!path.EndsWith("\\")) path += "\\";
return path;
}
public void LogMessageToFile(string msg)
{
System.IO.StreamWriter sw = System.IO.File.AppendText(
GetTempPath() + "My Log File.txt");
Console.Write(GetTempPath());
try
{
string logLine = System.String.Format(
"{0:G}: {1}.", System.DateTime.Now, msg);
sw.WriteLine(logLine);
}
finally
{
sw.Close();
}
}
}
}
CODEwithADDEDlogging.cs
using LoggerSpace;
using System.Diagnostics;
private void button2_Click(object sender, EventArgs y)
{
//LOG PART
var st = new StackTrace();
var sf = st.GetFrame(0);
var currentMethodName = sf.GetMethod();
var instance = new Logger();
instance.LogMessageToFile("Button Clicked, Clicktrader, from:"+ this.GetType().Name+ currentMethodName);
}

Use the CallerMemberNameAttribute for this. Something like this:
void LogSomething(string message, [CallerMemberName]string caller="")
{
// caller will have the function or property name of the caller to LogSomething
}
You can also get the source file name and line number with other attributes, all described in the link.

public void LogMessageToFile(string msg,
[CallerMemberName]string propertyName = null
[CallerFilePath] string sourceFilePath = ""
[CallerLineNumber] int sourceLineNumber = 0)
{
}
lets you catch the calling function, the file it was called from and the line number within that file.

Related

NULL symbol in file using StreamWriter

I want to add simple logger in to my app.
For this purpose I want to use StreamWriter.
Code:
private StreamWriter OutputStream;
OutputStream = new StreamWriter(this.LogFilePath, true);
// .... message - log from app
DateTime now = DateTime.Now;
message = string.Format("[{0:yyyy-MM-dd H:mm:ss}] {1}", now, message
if (OutputStream != null)
{
OutputStream.WriteLine(message);
OutputStream.Flush();
}
As result all strings are correctly captured and output is correct, but sometimes it can write empty string with invisible characters at the end:
sample:
[1970-08-31 14:56:26] Command response -> !c:65:f9:1b:82:97
and if i check this with some tool that can show invisible characters, I can see next:
As result ~600 lines of log - 125 mb.
I have found that reason could be next:
That happens. When you append a file first its size is corrected in
the directory (and that's transactional in NTFS) and then the actual
new data is written. There's good chance that if you shut down the
system you end up with a file appended with lots of null bytes because
data writes are not transactional unlike metadata (file size) writes.
There's no absolute solution to this problem.
Also tried to
check characters with isControl other similar checks;
tried to Trim last characters;
checked docs - looks like all correct
Any advice?
In case someone faced with same issue - reason for me unknown and i may only guess.... but I rewrite logic with log system and bug disappear:
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using UnityEngine;
public class EventLogger : MonoBehaviour
{
private string logFileName = "btlog.txt";
public bool EchoToConsole = true;
public bool AddTimeStamp = true;
public bool EnableFileStorage = true;
private string LogFilePath
{
get
{
return Path.Combine(Application.persistentDataPath, logFileName);
}
}
private static EventLogger Singleton = null;
const string format = "yyyy-MM-dd HH:mm:ss.fffffff";
public static EventLogger Instance
{
get { return Singleton; }
}
void Awake()
{
if (Singleton != null)
{
UnityEngine.Debug.LogError("Multiple EventLogger Singletons exist!");
return;
}
Singleton = this;
if (this.EnableFileStorage)
{
if (File.Exists(LogFilePath))
{
long length = new FileInfo(LogFilePath).Length;
int limit = 1024 * 1024 * 5; // 5mb
if (length > limit)
{
File.Delete(LogFilePath);
Log("log file removed");
}
}
Log("-------------------");
Log("NEW SESSION STARTED");
}
}
private async Task Write(string message)
{
if (this.EnableFileStorage)
{
if (AddTimeStamp)
{
DateTime now = DateTime.Now;
string strDate = now.ToString(format);
string trimmed = new string(message.Where(c => !char.IsControl(c)).ToArray());
message = string.Format("[{0}] {1}", strDate, trimmed);
}
using (StreamWriter outputStream = new StreamWriter(this.LogFilePath, true))
{
await outputStream.WriteLineAsync(message);
}
if (EchoToConsole)
{
UnityEngine.Debug.Log(message);
}
}
}
[Conditional("DEBUG"), Conditional("PROFILE")]
public static void Log(string Message)
{
if (EventLogger.Instance != null)
{
_ = EventLogger.Instance.Write(Message);
}
else
{
UnityEngine.Debug.Log(Message);
}
}
}

Where is the Unity IAP package located?

I'm currently working on an editor script that will ease my transition between freemium and paid versions of my game.I would like to manually import the .unitypackage file that gets imported when I click the import button under Services -> In-App Purchasing.
I am aware of the function AssetDatabase.ImportAsset(path) but I need the path of the package first.
Thanks in advance!
When you enable IAP and click Import, the following will happen:
1.Unity will generate a random file name with
FileUtil.GetUniqueTempPathInProject()
2.A full path will be constructed like this:
<ProjectName>Temp\FileUtil.GetUniqueTempPathInProject()
3.Unity will then add .unitypackage to the end of that random file name.
Now, you have something like:
<ProjectName>Temp\FileUtil.GetUniqueTempPathInProject()+".unitypackage";
4.IAP package will then be downloaded and stored to path from #3.
Illustration of what it looks like:
5.The file is then copied to
<ProjectName>Temp\TarGZ
It is saved with the file name generated from #2. No .unitypackage at the end like #3.
UnityEngine.Cloud.Purchasing
6.After that, Unity imports it with AssetDatabase.ImportPackage not AssetDatabase.ImportAsset(path,false) as mentioned in your question.
Now you can see where Unity IAP package is located but there are just few problems in what you are trying to do:
1.For the IAP package to be present, the import button from the Services Tab must be clicked. I am sure you want this to be automated.
2.The file name is not static therefore making the path hard to retrieve. As you can see from the image above, there are other files in this folder too. We don't know which one is the AIP Package.
3.When you re-start Unity, the temp folder will be deleted. So the downloaded IAP package will be lost.
The best way to get the Unity IAP package is to download it directly from Unity's server then save it to your preferred location. The code below will download the IAP package and store it at: <ProjectName>/UnityEngine.Cloud.Purchasing.unitypackage
It will also import it and then, enable it. For AIP to work, Analytics must be enabled too. This code will also do that.
I tried to hide the AIP url so that it won't be abused and Unity won't have change it.
If you want to remake this into something else, the two most important functions to look at are downloadAndInstallAIP() and deleteAndDisableAIP().
AIPDownloader Code:
using System;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using UnityEditor;
using UnityEditor.Analytics;
using UnityEditor.Purchasing;
using UnityEngine;
[ExecuteInEditMode]
public class AIPDownloader : MonoBehaviour
{
static string projectDirectory = Environment.CurrentDirectory;
static string aipFileName = "UnityEngine.Cloud.Purchasing.unitypackage";
static string etagName = "UnityEngine.Cloud.PurchasingETAG.text";
static string aipfullPath = "";
static string eTagfullPath = "";
static EditorApplication.CallbackFunction doneEvent;
[MenuItem("AIP/Enable AIP")]
public static void downloadAndInstallAIP()
{
//Make AIP fullpath
aipfullPath = null;
aipfullPath = Path.Combine(projectDirectory, aipFileName);
//Make AIP Etag fullpath
eTagfullPath = null;
eTagfullPath = Path.Combine(projectDirectory, etagName);
/*If the AIP File already exist at <ProjectName>/UnityEngine.Cloud.Purchasing.unitypackage,
* there is no need to re-download it.
Re-import the package
*/
if (File.Exists(aipfullPath))
{
Debug.Log("AIP Package already exist. There is no need to re-download it");
if (saveETag(null, true))
{
importAIP(aipfullPath);
return;
}
}
string[] uLink = {
"aHR0cHM=",
"Oi8vcHVibGljLWNkbg==",
"LmNsb3Vk",
"LnVuaXR5M2Q=",
"LmNvbQ==",
"L1VuaXR5RW5naW5l",
"LkNsb3Vk",
"LlB1cmNoYXNpbmc=",
"LnVuaXR5cGFja2FnZQ=="
};
prepare(uLink);
try
{
ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback(ValidateRemoteCertificate);
WebClient client = new WebClient();
client.DownloadFileCompleted += new AsyncCompletedEventHandler(OnDoneDownloading);
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(OnDownloadProgressChanged);
client.DownloadFileAsync(new Uri(calc(uLink)), aipfullPath);
}
catch (Exception e)
{
Debug.LogError("Error: " + e.Message);
}
}
[MenuItem("AIP/Disable AIP")]
public static void deleteAndDisableAIP()
{
FileUtil.DeleteFileOrDirectory("Assets/Plugins/UnityPurchasing");
//Disable AIP
PurchasingSettings.enabled = false;
//Disable Analytics
AnalyticsSettings.enabled = false;
}
private static bool ValidateRemoteCertificate(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors error)
{
return true;
}
public bool isAIPEnabled()
{
return PurchasingSettings.enabled;
}
private static bool saveETag(WebClient client, bool alreadyDownloadedAIP = false)
{
string contents = "";
if (alreadyDownloadedAIP)
{
//Load Etag from file
try
{
contents = File.ReadAllText(eTagfullPath);
return _saveEtag(contents, alreadyDownloadedAIP);
}
catch (Exception e)
{
Debug.LogWarning("File does not exist!: " + e.Message);
}
return false; //Failed
}
else
{
//Load Etag from downloaded WebClient
contents = client.ResponseHeaders.Get("ETag");
return _saveEtag(contents, alreadyDownloadedAIP);
}
}
static bool _saveEtag(string contents, bool alreadyDownloadedAIP = false)
{
if (contents != null)
{
try
{
//Save if not downloaded
if (!alreadyDownloadedAIP)
{
Directory.CreateDirectory(Path.GetDirectoryName(eTagfullPath));
File.WriteAllText(eTagfullPath, contents);
}
//Save to the etag to AIP directory
Directory.CreateDirectory(Path.GetDirectoryName("Assets/Plugins/UnityPurchasing/ETag"));
File.WriteAllText("Assets/Plugins/UnityPurchasing/ETag", contents);
return true;//Success
}
catch (Exception e)
{
Debug.LogWarning("Failed to write to file: " + e.Message);
return false; //Failed
}
}
else
{
return false; //Failed
}
}
public static void OnDownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
Debug.Log("Downloading: " + e.ProgressPercentage);
}
public static void OnDoneDownloading(object sender, AsyncCompletedEventArgs args)
{
WebClient wc = (WebClient)sender;
if (wc == null || args.Error != null)
{
Debug.Log("Failed to Download AIP!");
return;
}
Debug.Log("In Download Thread. Done Downloading");
saveETag(wc, false);
doneEvent = null;
doneEvent = new EditorApplication.CallbackFunction(AfterDownLoading);
//Add doneEvent function to call back!
EditorApplication.update = (EditorApplication.CallbackFunction)Delegate.Combine(EditorApplication.update, doneEvent);
}
static void AfterDownLoading()
{
//Remove doneEvent function from call back!
EditorApplication.update = (EditorApplication.CallbackFunction)Delegate.Remove(EditorApplication.update, doneEvent);
Debug.Log("Back to Main Thread. Done Downloading!");
importAIP(aipfullPath);
}
//Import or Install AIP
public static void importAIP(string path)
{
AssetDatabase.ImportPackage(path, false);
Debug.Log("Done Importing AIP package");
//Enable Analytics
AnalyticsSettings.enabled = true;
//Enable AIP
PurchasingSettings.enabled = true;
}
private static void prepare(string[] uLink)
{
for (int i = 5; i < uLink.Length; i++)
{
byte[] textAsBytes = System.Convert.FromBase64String(uLink[i]);
uLink[i] = Encoding.UTF8.GetString(textAsBytes);
}
for (int i = 0; i < uLink.Length; i++)
{
byte[] textAsBytes = System.Convert.FromBase64String(uLink[i]);
uLink[i] = Encoding.UTF8.GetString(textAsBytes);
if (i == 4)
{
break;
}
}
}
private static string calc(string[] uLink)
{
return string.Join("", uLink);
}
}

where to call the logging methods inside of catch or inside of every method in c#?

Actually, I want to log the data in such a way that it should have the methods that the application goes through in c#, and if there is an error then the error content also should be logged. the problem is where to call the log methods inside of catch or inside of every method? as I have nearly 200 methods.
I wrote the code like this:
public static bool Logging(System.Reflection.MethodBase methodInfo)
{
var fullMethodName = methodInfo.DeclaringType.FullName + "." + methodInfo.Name;
if (error_flag == false)
{
using (StreamWriter outputFile = new StreamWriter(path + #"\log.txt", true))
{
outputFile.WriteLine(fullMethodName + ": OK");
}
}
else
{
using (StreamWriter outputFile = new StreamWriter(path + #"\log.txt", true))
{
outputFile.WriteLine("\n\n --> Error in : " + fullMethodName);
}
}
return true;
}
//Logging Method 2
public static bool WriteErrorLog(Exception ErrorMessage)
{
using (StreamWriter outputFile = new StreamWriter(path + #"\log.txt", true))
{
outputFile.WriteLine("{0} Exception caught.", ErrorMessage);
}
return true;
}
and I have to call those methods from where??
I would suggest the following approach:
static void Main()
{
try
{
Log.Info("Start of process");
string input = StepOne();
StepTwo(input);
Log.Info("End of process");
}
catch(Exception e)
{
Log.Error(e);
}
}
public static string StepOne()
{
Log.Info("StepOne Completed");
return "Step One";
}
public static void StepTwo(string input)
{
Log.Info("StepTwo, input: " + input);
throw new ArgumentNullException("input");
}
Rather than rolling your own, use an existing logging framework, for example Log4Net.
Add a try catch at the highest possible layer, let errors bubble up until you can actually do something sensible with them.
Add logging messages to functions where it is sensible to do so, this will be the most useful information, for example you can log method inputs.
Avoid using reflection to get the method name, you probably don't need to log individual method names. All of that information will be in the stack trace anyway.

HoloLens -- Capturing Photo in Unity and Saving to Disk

I'm trying to save a captured photo to the disk on HoloLens. I'm getting the following exception error when trying to save a photo in Unity, based on this example. For a while I couldn't get the correct directory to save the file in, and now I think I've finally got that part working (see the getFolderPath function below). However, now I get this exception quoted below. Any ideas on how to fix this?
I'm using the Origami example code and I've simply attached this script PhotoCaptureTest.cs to the OrigamiCollection game object in Unity. Does anyone know how to fix this or how I can debug this more easily?
Exception thrown: 'System.NullReferenceException' in UnityEngine.dll
NullReferenceException: Object reference not set to an instance of an
object. at
UnityEngine.VR.WSA.WebCam.PhotoCapture.InvokeOnCapturedPhotoToDiskDelegate(OnCapturedToDiskCallback
callback, Int64 hResult) at
UnityEngine.VR.WSA.WebCam.PhotoCapture.$Invoke9(Int64 instance, Int64*
args) at UnityEngine.Internal.$MethodUtility.InvokeMethod(Int64
instance, Int64* args, IntPtr method) (Filename: Line: 0)
using UnityEngine;
using System.Collections;
using UnityEngine.VR.WSA.WebCam;
using System.Linq;
using Windows.Storage;
using System;
using System.IO;
public class PhotoCaptureTest : MonoBehaviour {
PhotoCapture photoCaptureObject = null;
string folderPath = "";
bool haveFolderPath = false;
// Use this for initialization
void Start ()
{
getFolderPath();
while (!haveFolderPath)
{
Debug.Log("Waiting for folder path...");
}
Debug.Log("About to call CreateAsync");
PhotoCapture.CreateAsync(false, OnPhotoCaptureCreated);
Debug.Log("Called CreateAsync");
}
// Update is called once per frame
void Update () {
}
async void getFolderPath()
{
StorageLibrary myPictures = await Windows.Storage.StorageLibrary.GetLibraryAsync(Windows.Storage.KnownLibraryId.Pictures);
Windows.Storage.StorageFolder savePicturesFolder = myPictures.SaveFolder;
Debug.Log("savePicturesFolder.Path is " + savePicturesFolder.Path);
folderPath = savePicturesFolder.Path;
haveFolderPath = true;
}
void OnPhotoCaptureCreated(PhotoCapture captureObject)
{
photoCaptureObject = captureObject;
Resolution cameraResolution = PhotoCapture.SupportedResolutions.OrderByDescending((res) => res.width * res.height).First();
CameraParameters c = new CameraParameters();
c.hologramOpacity = 0.0f;
c.cameraResolutionWidth = cameraResolution.width;
c.cameraResolutionHeight = cameraResolution.height;
c.pixelFormat = CapturePixelFormat.BGRA32;
captureObject.StartPhotoModeAsync(c, false, OnPhotoModeStarted);
}
void OnStoppedPhotoMode(PhotoCapture.PhotoCaptureResult result)
{
photoCaptureObject.Dispose();
photoCaptureObject = null;
}
private void OnPhotoModeStarted(PhotoCapture.PhotoCaptureResult result)
{
if (result.success)
{
string filename = string.Format(#"\CapturedImage{0}_n.jpg", Time.time);
string filePath = folderPath + filename;
string currentDir = Directory.GetCurrentDirectory();
Debug.Log("Current working direcotry is " + currentDir);
Debug.Log("Saving photo to " + filePath);
try
{
photoCaptureObject.TakePhotoAsync(filePath, PhotoCaptureFileOutputFormat.JPG, OnCapturedPhotoToDisk);
}
catch (System.ArgumentException e)
{
Debug.LogError("System.ArgumentException:\n" + e.Message);
}
}
else
{
Debug.LogError("Unable to start photo mode!");
}
}
void OnCapturedPhotoToDisk(PhotoCapture.PhotoCaptureResult result)
{
if (result.success)
{
Debug.Log("Saved Photo to disk!");
photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode);
}
else
{
Debug.Log("Failed to save Photo to disk");
}
}
}
Be sure to attach Visual Studio to unity and set breakpoints right before you save your file, and check your references. I suspect it's coming up null, also be sure to enable WebCam + Photos support in your manifest, that might be causing a null as well.
I find that your code to capture and save photos should be correct and I ran into the same error as you.
To avoid the error, I was able to save photos by changing the directory to Application.persistentDataPath, i.e., modifying Start() to the following
// ...
void Start()
{
folderPath = Application.persistentDataPath;
PhotoCapture.CreateAsync(false, OnPhotoCaptureCreated);
}
// ...
The photo is then available at the path UserFiles\LocalAppData\<APPNAME>\LocalState\, which can be found through the Hololens Device Portal's File Explorer.

Log file gets created but remains empty?

I implemented a Utility class in my Windows Form app with a log method. It seems to be creating the log.txt file fine, but not writing anything to it. No other programs are using this particular text file.
using System;
using System.IO;
using System.Text;
namespace program1{
static class Utils {
static Utils() { }
private static readonly string FilePath = TestEnvironment.PATH + #"\log.txt";
private static void CheckFile()
{
if (File.Exists(FilePath)) return;
using (FileStream fs = File.Create(FilePath)) {
Byte[] info = new UTF8Encoding(true).GetBytes("");
fs.Write(info, 0, info.Length);
fs.Close();
}
}
public static string Log(string code, string message) {
StreamWriter _w = File.AppendText(FilePath);
CheckFile();
string log = ("\r\n" + code + ": \n");
log += String.Format("{0} {1}\n", DateTime.Now.ToLongTimeString(),
DateTime.Now.ToLongDateString());
log += String.Format(" :{0}\n", message);
log += String.Format("-------------------------------");
_w.WriteLine(log);
_w.Close();
return log;
}
public static string LogDump() {
StreamReader _r = File.OpenText(FilePath);
string output = "";
string line;
while ((line = _r.ReadLine()) != null) {
output += line;
}
_r.Close();
return output;
}
}
}
Is it perhaps not liking the String.Formats?
According to MSDN:
A stream’s encoder is not flushed unless you explicitly call Flush or dispose of the object.
Either dispose the StreamWriter instance you're creating (ideally by enclosing it in a using block, or alternatively by explicitly calling Dispose()):
using (StreamWriter _w = File.AppendText(FilePath))
{
...
}
Or explicitly call Flush():
_w.Flush();
You don't need the CheckFile() method. AppendText() will create the file if necessary.
The real problem is how you're writing the file. Change your method to this:
public static string Log(string code, string message)
{
string log;
using (var writer = File.AppendText(FilePath))
{
log = ("\r\n" + code + ": \n");
log += String.Format("{0} {1}\n", DateTime.Now.ToLongTimeString(),
DateTime.Now.ToLongDateString());
log += String.Format(" :{0}\n", message);
log += String.Format("-------------------------------");
writer.WriteLine(log);
}
return log;
}
To clarify, the using block calls Dispose() on the StreamWriter. This flushes content to the file.

Categories