আমি জাভাতে কীভাবে বর্তমান স্ট্যাক ট্রেস পেতে পারি?


1001

জাট-এ কীভাবে বর্তমান স্ট্যাকের ট্রেস পাব Environment.StackTrace? নেট আপনি কীভাবে করতে পারেন তেমন ?

আমি খুঁজে পেয়েছি Thread.dumpStack()তবে এটি আমি যা চাই তা নয় - আমি স্ট্যাকের ট্রেসটি ফিরে পেতে চাই, এটি প্রিন্ট করে না।


73
আমি যখনই একটিগ্রহে ডিবাগ করছি তখন আমি ঘড়ির ভাব হিসাবে "নতুন এক্সেপশন ()। প্রিন্টস্ট্যাকট্রেস ()" ব্যবহার করি। আপনি যখন কোনও ব্রেকপয়েন্টে স্থগিত হন এবং আপনি কোথা থেকে এসেছেন তা জানতে চাইলে এটি সুবিধাজনক।
টিম বাথ

22
@ টিমবিথ: আপনি ডিবাগ মোডে থাকাকালীন গ্রহনটি ইতিমধ্যে আপনাকে স্ট্যাক ট্রেসটি বলছে না? আমি তাই মনে করি.
লুইজি ম্যাসা গ্যালেরানো

41
Arrays.toString (Thread.currentThread () getStackTrace ()।); যথেষ্ট সহজ
অজয়

2
@ আরকানন এটি জাভা বোঝায় এবং দেখায় যে তারা কীভাবে এটি নেট এ করবে এবং জাভাতে সমতুল্য কী হবে।
পোকেছু 22

9
এটিকে সুন্দরভাবে মুদ্রণের জন্য আপনি অ্যাপাচি স্ট্রিংগিলগুলি ব্যবহার করতে পারেন: স্ট্রিংইটিলস.জাইন (কারেন্টথ্রেড ()। গেটস্ট্যাক ট্রেস (), "\ n");
আর্ট

উত্তর:


1158

আপনি ব্যবহার করতে পারেন Thread.currentThread().getStackTrace()

এটি একটি অ্যারের প্রদান করে StackTraceElementযা কোনও প্রোগ্রামের বর্তমান স্ট্যাক ট্রেসকে উপস্থাপন করে।


48
একটি শীতল এক-লাইনের যদি আপনি ইতিমধ্যে Apache কমন্স ব্যবহার করছেন একটি স্ট্রিং পেতে: String fullStackTrace = org.apache.commons.lang.exception.ExceptionUtils.getFullStackTrace(e); stackoverflow.com/a/10620951/11236
ripper234

5
অ্যারেতে প্রথম উপাদান (সূচক 0) হ'ল java.lang.Thread.getStackT્રેસ পদ্ধতি, দ্বিতীয় (সূচক 1) সাধারণত প্রথম আগ্রহ।
লিল্লিনাক্স

175
স্ট্যাক ট্রেসকে স্ট্রিংয়ে রূপান্তর করার জন্য একটি লাইনার যখন আপনার ব্যতিক্রম না থাকে এবং আপনি অ্যাপাচি অ্যারেস টু স্ট্রিং (থ্রেড.কন্ট্রেন্টথ্রেড ()। গেটস্ট্যাকট্রেস ()) ব্যবহার করেন না
টনি

9
@ টনির সমাধান আরও ভাল কারণ এর জন্য 20:51
mvd

3
যখন নীচে অনেক উত্তর তীক্ষ্ন - new Throwable().getStackTrace()কারণ এটি পরীক্ষা করার দরকার নেই অনেক দ্রুত হয়, this != Thread.currentThread()ও শিশু শ্রেণীর মাধ্যমে এটি কলিং (সম্ভাব্য জেভিএম overheads রোধ করা যাবে Exception extends Throwable)
Vlad

264
Thread.currentThread().getStackTrace();

আপনি যদি স্ট্যাকের প্রথম উপাদানটি কী তা যত্নশীল না করেন তবে ঠিক আছে।

new Throwable().getStackTrace();

যদি তা বিবেচনা করে তবে আপনার বর্তমান পদ্ধতির জন্য একটি সংজ্ঞায়িত অবস্থান থাকবে।


35
(new Throwable()).getStackTrace()খুব দ্রুত কার্যকর হচ্ছে (দেখুন bugs.sun.com/bugdatedia/view_bug.do?bug_id=6375302 )
পরাক্রমশালী

2
আপনি কীভাবে আরও বিস্তারিতভাবে ব্যাখ্যা করতে পারবেন যে থ্রেড.কন্ট্রেনথ্রেড () .গেটস্ট্যাকট্রেস () এর ফলাফলগুলি কীভাবে নতুন থ্রোয়েবল ()। GetStackTrace () থেকে আলাদা হতে পারে? আমি উভয়ই চেষ্টা করে দেখতে পেলাম যে থ্রেড.কন্ট্রেনথ্রেড ()। GetStackTrace () রিটার্নটি সূচক 0-এ থ্রেড। বিট স্ট্যাকট্রেস সহ অ্যারে এবং কলিং পদ্ধতিটি সূচী 1 এ রয়েছে The সূচক 0-এ পদ্ধতিটি দেখে মনে হয় যে উভয় পদ্ধতির বর্তমান পদ্ধতির জন্য একটি নির্ধারিত অবস্থান রয়েছে তবে অবস্থানগুলি পৃথক। আপনি দেখিয়ে দিচ্ছেন কি অন্য পার্থক্য আছে?
রায়ান

9
@ রায়ান, থার্ডড্রন্টট্রেনড থ্রেড ()। গেটস্ট্যাকট্রেস () পদ্ধতিটির বিভিন্ন সংস্করণে এই অবস্থানটি বজায় রাখার প্রতিশ্রুতি দেয় না এমন কোনও গ্যারান্টি নেই। সুতরাং আপনি যখন যাচাই করতে চান এটি সূচক 1 এ ছিল। সূর্যের জন্য জেভিএমের কিছু সংস্করণে (1.5 বা 1.6 মনে রাখবেন না), এটি সূচক 2 ছিল That এটিই আমি একটি সংজ্ঞায়িত অবস্থান বলতে চাইছি - স্পেক কলগুলি এটি সেখানে থাকতে হবে, এবং ভবিষ্যতে সেখানে থাকবে।
যিশাই

5
@MightyE বাগ এখন জাভা 6. সংশোধন হিসাবে অভ্যন্তরীণ এ খুঁজছি রিপোর্ট করা হয় যেমন বন্ধ, এটি এখন এটা দেখে মনে হচ্ছে না (new Exception()).getStackTrace()অনুরোধ করা হলে স্ট্যাক ট্রেস বর্তমান থ্রেডে আছে (যা সবসময় কেনার ক্ষেত্রে দেখা হবেThread.currentThread().getStackTrace();
এম জাস্টিন

3
@ এম জাস্টিন: বাগটি এখন ঠিক হিসাবে রিপোর্ট করা হয়নি, এটি মাইটি মন্তব্যটি লেখার ছয় বছর আগেও ঠিক করা হয়েছে। আসলে, স্ট্যাকওভারফ্লো প্রতিষ্ঠিত হওয়ার আগেই এটি ঠিক করা হয়েছিল ...
হোলার

181
for (StackTraceElement ste : Thread.currentThread().getStackTrace()) {
    System.out.println(ste);
}

20
জাভাতে একটি প্রিন্টস্ট্যাকট্রেস পদ্ধতি থ্রোয়েবলের উপর সংজ্ঞায়িত করা হয়েছে, আপনি এটি করতে পারেন: new Exception().printStackTrace(System.out)যা আমি মনে রাখতে পারি, লুপটির জন্য সম্ভবত ওভারহেড কম রয়েছে, তবে আপনার কেবল এটি ডিবাগিং জিনিস হিসাবে ব্যবহার করা উচিত ...
জাপ

2
আমি এটিকে সবচেয়ে পছন্দ করি কারণ আপনি আপনার for (StackTraceElement ste : Thread.currentThread().getStackTrace()) { if(ste.toString().contains("mypackages")){ System.out.println(ste); } }
মুদ্রণ

61
Thread.currentThread().getStackTrace();

JDK1.5 থেকে পাওয়া যায়।

একটি পুরোনো সংস্করণ জন্য, আপনি পুনর্নির্দেশ করতে পারেন exception.printStackTrace()একটি থেকে StringWriter():

StringWriter sw = new StringWriter();
new Throwable("").printStackTrace(new PrintWriter(sw));
String stackTrace = sw.toString();

3
new Throwable().getStackTrace()জেডি কে 1.4-এর পরে পাওয়া যায় ...
হোলার

40

আপনি এটির জন্য অ্যাপাচি কমন্স ব্যবহার করতে পারেন:

String fullStackTrace = org.apache.commons.lang3.exception.ExceptionUtils.getStackTrace(e);

13
এটি একটি দুর্দান্ত একটি লাইন সমাধান। এফওয়াইআই, যে কেউ ব্যবহার করছেন org.apache.commons.lang3তার জন্য পুরো লাইনটি হ'ল:org.apache.commons.lang3.exception.ExceptionUtils.getStackTrace(e);
ফাইল অফসেট

2
সরানোর সময় কেবল ফাইলঅফসেটের মন্তব্যটির পুনরাবৃত্তি করা org.apache.commons.lang3, যা আমি প্রাথমিকভাবে উপেক্ষা করেছি - আমদানির lang3পরিবর্তে আমদানি ব্যবহার করে langএবং এর সাথে প্রতিস্থাপন getFullStackTraceকরে getStackTrace
ggkmath

এটা খুব দরকারী! আইডিই ব্যবহার করে ডিবাগ করার সময় (উদাহরণস্বরূপ, ইন্টেলিজ আইডিইএ) ExceptionUtils.getStackTrace(new Throwable())স্ট্যাক ট্রেস পেতে এক্সপ্রেশনটি ব্যবহার করা দুর্দান্ত ।
সের্গেই ব্রুনভ

24

অ্যান্ড্রয়েডে আরও সহজ উপায় হ'ল এটি ব্যবহার করা:

import android.util.Log;
String stackTrace = Log.getStackTraceString(exception); 

4
GetStackTraceString সংজ্ঞায়িত কোথায়? এটি কি কোনও তৃতীয় পক্ষের লগিং লাইব্রেরি থেকে?
skiphoppy


22

সমস্ত থ্রেডের স্ট্যাক ট্রেস পেতে আপনি জেস্ট্যাক ইউটিলিটি, জে কনসোল ব্যবহার করতে পারেন বা একটি কিল-কোট সিগন্যাল (কোনও পোস্টিক্স অপারেটিং সিস্টেমে) প্রেরণ করতে পারেন।

তবে আপনি যদি এই প্রোগ্রামটিমেটিকভাবে করতে চান তবে আপনি থ্রেডএমএক্সবিয়ান ব্যবহার করে দেখতে পারেন:

ThreadMXBean bean = ManagementFactory.getThreadMXBean();
ThreadInfo[] infos = bean.dumpAllThreads(true, true);

for (ThreadInfo info : infos) {
  StackTraceElement[] elems = info.getStackTrace();
  // Print out elements, etc.
}

উল্লিখিত হিসাবে, আপনি যদি কেবল বর্তমান থ্রেডের স্ট্যাক ট্রেস চান তবে এটি অনেক সহজ - কেবল ব্যবহার করুন Thread.currentThread().getStackTrace();


2
এই উত্তরের কোড স্নিপেটটি JVM কেআইভিটি সিগন্যাল (সিস্টেমের মতো ইউনিক্সে) বা উইন্ডোজে <ctrl> <ব্রেক>> প্রেরণে আপনি যে ধরণের ডাম্প দেখেন তা প্রোগ্রামালিমেটিকভাবে উত্সাহিত করে। আপনি মনিটর এবং সিঙ্ক্রোনাইজার পাশাপাশি কেবল স্ট্যাকের চিহ্নগুলি দেখতে পাবেন এমন অন্যান্য জবাবগুলির বিপরীতে (যেমন আপনি যদি স্ট্যাকট্রেস এলিমেন্ট অ্যারেতে পুনরাবৃত্তি করেন এবং System.err.println(elems[i])প্রতিটি ব্যবহার করেন)। স্নিপেটের দ্বিতীয় লাইনটি এর bean.আগে অনুপস্থিত dumpAllThreadsতবে আমার ধারণা বেশিরভাগ লোকেরা এটি কাজ করতে পারে।
জর্জ হকিন্স

22

অন্য একটি সমাধান (কেবলমাত্র 35 31 অক্ষর):

new Exception().printStackTrace();   
new Error().printStackTrace();

1
দুর্দান্ত সমাধান এখন আমাকে সমস্ত স্ট্যাক ফ্রেমের মধ্য দিয়ে লুপ করতে হবে না
ভিনিল কোভভুরি


17

টনি, গৃহীত উত্তরের মন্তব্য হিসাবে, সেরা উত্তর বলে মনে হচ্ছে যা আসলে ওপি-র প্রশ্নের উত্তর দেয় :

Arrays.toString(Thread.currentThread().getStackTrace()).replace( ',', '\n' );

... ওপি হয়নি না কিভাবে পেতে জিজ্ঞাসা Stringএকটি থেকে স্ট্যাক ট্রেস থেকে Exception। এবং যদিও আমি অ্যাপাচি কমন্সের একটি বিশাল অনুরাগী, যখন উপরের মতো সাধারণ কিছু রয়েছে তখন বাইরের পাঠাগারটি ব্যবহার করার কোনও যৌক্তিক কারণ নেই।


আমার থ্রেডগুলির একটি তালিকা রয়েছে এবং আমার তাদের স্ট্যাকের চিহ্নগুলি মুদ্রণ করতে হবে।
ড্যানিয়েল এস ইয়াইটসকভ

সেক্ষেত্রে নিশ্চিত হয়ে নিন যে আপনি ব্যবহার করেছেন Thread.getAllStackTraces()যা আপনাকে এ Map<Thread, StackTraceElement[]>। হটস্পট সমস্ত থ্রেডকে সাফল্যযুক্ত করবে যখনই এটির একটি সাফপয়েন্ট দরকার হয়। জিং অন্যকে বিরক্ত না করে স্বতন্ত্র থ্রেডগুলিকে সাফপয়েন্টে নিয়ে আসতে পারে। স্ট্যাকট্রেস পেতে একটি সাফপয়েন্ট requires Thread.getAllStackTraces()সমস্ত স্ট্যাক ট্রেস পায় এবং সমস্ত থ্রেড কেবল একবার বন্ধ করে দেয়। অবশ্যই আপনাকে অবশ্যই কোন ম্যাপিংয়ের বিষয়ে আগ্রহী তা অবশ্যই খুঁজে বার করতে হবে
রাল্ফ এইচ

14

আমার পরামর্শ হচ্ছে

  Thread.dumpStack()

একটি সহজ উপায় এবং আসলে কোনও সমস্যা নাও হতে পারে আসলে একটি ব্যতিক্রম বা থ্রোয়েবল না বানানোর সুবিধা রয়েছে এবং এটি আরও উল্লেখযোগ্য বিষয়।


1
ঠিক আছে, আসলে, ডাম্পস্ট্যাক () একটি স্ট্যাটিক পদ্ধতি, এবং এটি একটি স্থির উপায়ে অ্যাক্সেস করা উচিত। ধন্যবাদ, গ্রহন!
থমাস অ্যাডকিন্স

1
আমি শর্টকাটটি পছন্দ করি তবে এই পদ্ধতির উত্স কোডটি: new Exception("Stack trace").printStackTrace();সুতরাং এটি আসলে একটি থ্রোয়েবল তৈরি করছে
ফ্লোরেন্ট

13

স্ট্যাকট্রেস পাওয়া:

StackTraceElement[] ste = Thread.currentThread().getStackTrace();

মুদ্রণ স্ট্যাকট্রেস (জাভা 8+):

Arrays.asList(ste).forEach(System.out::println);

মুদ্রণ স্ট্যাকট্রেজ (জাভা 7):

StringBuilder sb = new StringBuilder();

for (StackTraceElement st : ste) {
    sb.append(st.toString() + System.lineSeparator());
}
System.out.println(sb);


10

আমার কাছে একটি ইউটিলিটি পদ্ধতি রয়েছে যা স্ট্যাকট্রেসের সাহায্যে একটি স্ট্রিং প্রদান করে:

static String getStackTrace(Throwable t) {
    StringWriter sw = new StringWriter();
    PrintWriter pw = new PrintWriter(sw, true);
    t.printStackTrace(pw);
    pw.flush();
    sw.flush();
    return sw.toString();
}

এবং ঠিক যেমন লগিট ...

... 
catch (FileNotFoundException e) {
    logger.config(getStackTrace(e));
}

এটি দেখতে নেটবিন্স দ্বারা উত্পাদিত একটি অটোর মতো।
লাইফ গ্রুইনউল্ড্ট

লগার হ'ল বিল্ট ইন লগার বা একটি যা আপনি তৈরি করেছেন এবং একটি ফাইলে লিখেন।
ডগ হাউফ

@ ডউগ হাফ, লগার জাভা লগারে নির্মিত একটি উল্লেখ। আপনি লগিং.প্রপার্টি ফাইলটি কনফিগার করেছেন তা নিশ্চিত করুন। আপনার শ্রেণির শুরুতে এটি যুক্ত করুন: ব্যক্তিগত স্ট্যাটিক ফাইনাল লগার লগার = লগার.সেটলোগার (আপনার_ক্লাস_নাম.ক্লাস.সেটনাম ());
সালভাদোর ভ্যালেন্সিয়া

1
java.util.logging.Logger#log(Level, String, Throwable)ইতিমধ্যে এটি করে। এটি জাভা স্ট্যান্ডার্ড লাইব্রেরিতে অকারণে ইতিমধ্যে বিদ্যমান জিনিসগুলিকে আবার লিখন করছে এবং নতুন বিকাশকারীকে ভুল দিকে নির্দেশ করছে যা ডকুমেন্টেশন থেকে দূরে রয়েছে। এখন এটি করে আপনি স্ট্যাকট্রেসকে একটি নির্দিষ্ট উপায়ে ফর্ম্যাট করতে বাধ্য করেছেন, যেখানে loggingএপিআই আপনাকে লগ স্টেটমেন্টের বিন্যাসকে স্বনির্ধারণের সাথে পরিবর্তনশীলভাবে পরিবর্তন করতে দেয়। আমার জ্ঞানের সাথে, log4jএকইরকম আচরণও রয়েছে। চাকাটি পুনরায় উদ্ভাবন করবেন না কারণ আপনি যে জিনিসগুলি আমি ব্যাখ্যা করেছি তা হারানো শেষ করে।
searchengine27

1
এটি 2012 সালেও বিদ্যমান ছিল। দুর্ভাগ্যক্রমে তারিখটি আপনার ক্ষেত্রে কোনও পার্থক্য তৈরি করে না। [লিঙ্ক] দেখুন (ডকস.ওরাকল.com/ জাভাস / ১.০.০ / ডকস / অ্যাপি / জাভা / ইউটিল / ব্লগিং / লোগার এইচটিএমএল#log (java.util.logging.Level, java.lang.String, java.lang .ট্রোয়েবল)), [লিঙ্ক] (ডকস.ওরকল / জাজাসে / ১.০.০/২ ডকসস / এপি / জাভা / ইউটিলি / ব্লগিং / লোগার এইচটিএমএল ব্লগ (জাভা.ইটিল.লগিং.লোগ্র্যাকর্ড)), ইত্যাদি (সেখানে) বিভিন্ন ফাংশন যে পাস হয় Throwableবন্ধ Handlerকরার s থেকে Formatterবেশি আমি এখানে তালিকাবদ্ধ করেছি 2012 যা Java7 ছিল, মধ্যে s এবং এই ফাংশন ফিরে পর্যন্ত বেশি Java7 হয়েছে প্রায় ডেট;। অত: পর এ J2SE 5.0 javadocs)
searchengine27

10

পেয়ারার সাথে স্ট্রিং করতে:

Throwables.getStackTraceAsString(new Throwable())

8
try {
}
catch(Exception e) {
    StackTraceElement[] traceElements = e.getStackTrace();
    //...
}

অথবা

Thread.currentThread().getStackTrace()

আপনার শীর্ষ উদাহরণটি কি কেবল চেষ্টা / ধরা ব্লকের সাথে সম্পর্কিত স্ট্যাক ট্রেস দেবে না?
ড্যান মোনেগো

1
দুজনের মধ্যে পার্থক্য কী
twlkyao

5

আপনি এটি চেষ্টা করতে পারে:

catch(Exception e)
{
    StringWriter writer = new StringWriter();
    PrintWriter pw = new PrintWriter(writer);
    e.printStackTrace(pw);
    String errorDetail = writer.toString();
}

স্ট্রিং 'ত্রুটি-বিবরণ' স্ট্যাকট্রেস ধারণ করে।


5
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();

অ্যারের শেষ উপাদানটি স্ট্যাকের নীচে প্রতিনিধিত্ব করে, যা ক্রমটিতে সাম্প্রতিকতম পদ্ধতির প্রার্থনা method

A StackTraceElement has getClassName(), getFileName(), getLineNumber() and getMethodName().

স্ট্যাকট্রেসএলিমেন্টের মধ্য দিয়ে লুপ করুন এবং আপনার পছন্দসই ফলাফলটি পান।

for (StackTraceElement ste : stackTraceElements ) 
{
    //do your stuff here...
}

সর্বশেষ উপাদান উল্লেখ করার জন্য ধন্যবাদ। পাল্টা স্বজ্ঞাত এবং আমাকে কিছু সময় বাঁচিয়েছে।
স্পষ্টলাইট

এছাড়াও। স্ট্রিং () সমস্ত তথ্যটি স্ট্রিংয়ে সুন্দরভাবে আউটপুট দেয়
ভ্লাদ

4

আমি উপরের থেকে উত্তরগুলি ব্যবহার করেছি এবং ফরম্যাটিং যুক্ত করেছি

public final class DebugUtil {

    private static final String SEPARATOR = "\n";

    private DebugUtil() {
    }

    public static String formatStackTrace(StackTraceElement[] stackTrace) {
        StringBuilder buffer = new StringBuilder();
        for (StackTraceElement element : stackTrace) {
            buffer.append(element).append(SEPARATOR);
        }
        return buffer.toString();
    }

    public static String formatCurrentStacktrace() {
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        return formatStackTrace(stackTrace);
    }
}

2

আপনি যদি আপনার প্রক্রিয়াটির বর্তমান কল স্ট্যাকটি পরীক্ষা করতে চান তবে আপনি jstack ইউটিলিটি ব্যবহার করতে পারেন।

Usage:
    jstack [-l] <pid>
        (to connect to running process)
    jstack -F [-m] [-l] <pid>
        (to connect to a hung process)
    jstack [-m] [-l] <executable> <core>
        (to connect to a core file)
    jstack [-m] [-l] [server_id@]<remote server IP or hostname>
        (to connect to a remote debug server)

Options:
    -F  to force a thread dump. Use when jstack <pid> does not respond (process is hung)
    -m  to print both java and native frames (mixed mode)
    -l  long listing. Prints additional information about locks
    -h or -help to print this help message

1

এটি একটি পুরানো পোস্ট, তবে এখানে আমার সমাধান:

Thread.currentThread().dumpStack();

আরও তথ্য এবং আরও পদ্ধতি এখানে: http://javarevisited.blogspot.fr/2013/04/how-to-get-current-stack-trace-in-java-thread.html


3
যেহেতু Thread.dumpStack () স্থিতিশীল, আপনার কেবল এটি সরাসরি কল করা উচিত।
ডেভিড আভেন্দাসোড়া

2
ওপি সূচিত করেছে যে তারা স্ট্যাক ট্রেসের কোডে একটি রেফারেন্স চায়, এটি প্রিন্ট না করে।
টেলর আর

1
@ ডেভিড অবেন্দাসোরা যেমন উল্লেখ করেছেন যে আপনার Thread.dumpStack()সরাসরি কল করা উচিত কারণ এটির স্থির পদ্ধতি।
এম-ওয়াজেইহ

হ্যাঁ, java.lang.Thread.dumpStack () জাভা ১১-এ কাজ করে
পার্ক জংবাম

-3

System.out.println(Arrays.toString(Thread.currentThread().getStackTrace()));

এটি আপনাকে লুপ ছাড়াই স্ট্যাক ট্রেস মুদ্রণ করতে সহায়তা করবে।


1
আপনার উত্তরটি 3 মাস আগে আমার পোস্টের মতোই।
মাইকে রডেন্ট
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.